/*
 * #%L
 * Alfresco Repository
 * %%
 * Copyright (C) 2005 - 2023 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.filesys.repo;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import jakarta.transaction.UserTransaction;

import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.experimental.categories.Category;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;

import org.alfresco.filesys.alfresco.ExtendedDiskInterface;
import org.alfresco.jlan.server.NetworkServer;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.auth.ClientInfo;
import org.alfresco.jlan.server.config.ServerConfiguration;
import org.alfresco.jlan.server.core.DeviceContextException;
import org.alfresco.jlan.server.core.SharedDevice;
import org.alfresco.jlan.server.filesys.AccessDeniedException;
import org.alfresco.jlan.server.filesys.AccessMode;
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
import org.alfresco.jlan.server.filesys.FileAction;
import org.alfresco.jlan.server.filesys.FileAttribute;
import org.alfresco.jlan.server.filesys.FileExistsException;
import org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.jlan.server.filesys.FileOpenParams;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.server.filesys.NetworkFileServer;
import org.alfresco.jlan.server.filesys.PermissionDeniedException;
import org.alfresco.jlan.server.filesys.SearchContext;
import org.alfresco.jlan.server.filesys.TreeConnection;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel;
import org.alfresco.repo.action.evaluator.NoConditionEvaluator;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.management.subsystems.ApplicationContextFactory;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.transfer.TransferModel;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.cmr.rule.RuleType;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.OwnableService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.test_category.BaseSpringTestsCategory;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.Pair;

/**
 * Unit tests for Alfresco Repository ContentDiskDriver
 */
@Category(BaseSpringTestsCategory.class)
public class ContentDiskDriverTest extends TestCase
{
    private static final String TEST_PROTOTYPE_NAME = "test";
    private static final String TEST_REMOTE_NAME = "remoteName";
    private static final String TEST_SERVER_NAME = "testServer";

    private static final String TEST_USER_AUTHORITY = "userx";

    private Repository repositoryHelper;
    private CifsHelper cifsHelper;
    private ExtendedDiskInterface driver;
    private NodeService mlAwareNodeService;
    private NodeService nodeService;
    private TransactionService transactionService;
    private ContentService contentService;
    private RuleService ruleService;
    private ActionService actionService;
    private PersonService personService;
    private MutableAuthenticationService authenticationService;
    private PermissionService permissionService;
    private OwnableService ownableService;
    private FileFolderService fileFolderService;
    private CheckOutCheckInService checkOutCheckInService;

    private static Log logger = LogFactory.getLog(ContentDiskDriverTest.class);

    final String SHARE_NAME = "test";
    final String STORE_NAME = "workspace://SpacesStore";
    final String ROOT_PATH = "/app:company_home";

    private ApplicationContext applicationContext;

    private final String TEST_ROOT_PATH = "ContentDiskDriverTest";
    private final String TEST_ROOT_DOS_PATH = "\\" + TEST_ROOT_PATH;

    @Override
    protected void setUp() throws Exception
    {
        applicationContext = ApplicationContextHelper.getApplicationContext();
        repositoryHelper = (Repository) this.applicationContext.getBean("repositoryHelper");
        ApplicationContextFactory fileServers = (ApplicationContextFactory) this.applicationContext.getBean("fileServers");
        cifsHelper = (CifsHelper) fileServers.getApplicationContext().getBean("cifsHelper");
        driver = (ExtendedDiskInterface) this.applicationContext.getBean("contentDiskDriver");
        mlAwareNodeService = (NodeService) this.applicationContext.getBean("mlAwareNodeService");
        nodeService = (NodeService) applicationContext.getBean("nodeService");
        transactionService = (TransactionService) applicationContext.getBean("transactionService");
        contentService = (ContentService) applicationContext.getBean("contentService");
        ruleService = (RuleService) applicationContext.getBean("ruleService");
        actionService = (ActionService) this.applicationContext.getBean("actionService");
        personService = (PersonService) this.applicationContext.getBean("personService");
        authenticationService = (MutableAuthenticationService) this.applicationContext.getBean("authenticationService");
        permissionService = (PermissionService) this.applicationContext.getBean("permissionService");
        ownableService = (OwnableService) this.applicationContext.getBean("ownableService");
        fileFolderService = (FileFolderService) this.applicationContext.getBean("fileFolderService");
        checkOutCheckInService = (CheckOutCheckInService) this.applicationContext.getBean("checkOutCheckInService");

        assertNotNull("content disk driver is null", driver);
        assertNotNull("repositoryHelper is null", repositoryHelper);
        assertNotNull("mlAwareNodeService is null", mlAwareNodeService);
        assertNotNull("nodeService is null", nodeService);
        assertNotNull("transactionService is null", transactionService);
        assertNotNull("contentService is null", contentService);
        assertNotNull("ruleService is null", ruleService);
        assertNotNull("actionService is null", actionService);
        assertNotNull("cifsHelper", cifsHelper);
        assertNotNull("checkOutCheckInService", checkOutCheckInService);

        AuthenticationUtil.setRunAsUserSystem();

        // remove our test root
        RetryingTransactionCallback<Void> removeRootCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef companyHome = repositoryHelper.getCompanyHome();
                NodeRef rootNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, TEST_ROOT_PATH);
                if (rootNode != null)
                {
                    logger.debug("Clean up test root node");
                    nodeService.deleteNode(rootNode);
                }
                return null;
            }
        };
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        tran.doInTransaction(removeRootCB, false, true);

    }

    @Override
    protected void tearDown() throws Exception
    {}

    private DiskSharedDevice getDiskSharedDevice() throws DeviceContextException
    {

        ContentContext ctx = new ContentContext("testContext", STORE_NAME, ROOT_PATH, repositoryHelper.getCompanyHome());

        DiskSharedDevice share = new DiskSharedDevice("test", driver, ctx);
        return share;
    }

    /**
     * Test Get File Information
     */
    public void testGetFileInformation() throws Exception
    {
        logger.debug("testGetFileInformation");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();

        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        class TestContext
        {
            NodeRef testNodeRef;
        }
        ;

        final TestContext testContext = new TestContext();

        /**
         * Test 1 : Get the root info
         */
        FileInfo finfo = driver.getFileInformation(testSession, testConnection, "");
        assertNotNull("root info is null", finfo);
        assertEquals("root has a unexpected file name", "", finfo.getFileName());

    }

    /**
     * Test Create File
     */
    public void testCreateFile() throws Exception
    {
        logger.debug("testCreatedFile");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();

        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        class TestContext
        {
            NodeRef testNodeRef;
        }
        ;

        final TestContext testContext = new TestContext();

        /**
         * Step 1 : Create a new file in read/write mode and add some content.
         */
        int openAction = FileAction.CreateNotExist;

        final String FILE_NAME = "testCreateFileX.new";
        final String FILE_PATH = "\\" + FILE_NAME;

        FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);

        final NetworkFile file = driver.createFile(testSession, testConnection, params);
        assertNotNull("file is null", file);
        assertFalse("file is read only, should be read-write", file.isReadOnly());
        assertFalse("file is not closed ", file.isClosed());

        RetryingTransactionCallback<Void> writeStuffCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                byte[] stuff = "Hello World".getBytes();
                driver.writeFile(testSession, testConnection, file, stuff, 0, stuff.length, 0);
                driver.closeFile(testSession, testConnection, file);
                return null;
            }
        };
        tran.doInTransaction(writeStuffCB);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef companyHome = repositoryHelper.getCompanyHome();
                NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
                testContext.testNodeRef = newNode;
                assertNotNull("can't find new node", newNode);
                Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT);
                assertNotNull("content is null", content);
                return null;
            }
        };
        tran.doInTransaction(validateCB);

        // now validate that the new node is in the correct location and has the correct name
        FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
        assertNotNull("info is null", info);

        NodeRef n2 = getNodeForPath(testConnection, FILE_PATH);
        assertEquals("get Node For Path returned different node", testContext.testNodeRef, n2);

        /**
         * Step 2 : Negative Test Attempt to create the same file again
         */
        try
        {
            driver.createFile(testSession, testConnection, params);
            fail("File exists not detected");
        }
        catch (FileExistsException fe)
        {
            // expect to go here
        }

        // Clean up so we could run the test again
        driver.deleteFile(testSession, testConnection, FILE_PATH);

        /**
         * Step 3 : create a file in a new directory in read only mode
         */
        String FILE2_PATH = TEST_ROOT_DOS_PATH + FILE_PATH;

        FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, openAction, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);
        driver.createDirectory(testSession, testConnection, dirParams);

        FileOpenParams file2Params = new FileOpenParams(FILE2_PATH, openAction, AccessMode.ReadOnly, FileAttribute.NTNormal, 0);
        NetworkFile file2 = driver.createFile(testSession, testConnection, file2Params);

        // clean up so we could run the test again
        driver.deleteFile(testSession, testConnection, FILE2_PATH);
    }

    /**
     * Unit test of delete file
     */
    public void testDeleteFile() throws Exception
    {
        logger.debug("testDeleteFile");

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();

        TreeConnection testConnection = testServer.getTreeConnection(share);

        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Step 1 : Create a new file in read/write mode and add some content.
         */
        int openAction = FileAction.CreateNotExist;
        String FILE_PATH = "\\testDeleteFile.new";

        FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);

        final NetworkFile file = driver.createFile(testSession, testConnection, params);
        assertNotNull("file is null", file);
        assertFalse("file is read only, should be read-write", file.isReadOnly());

        RetryingTransactionCallback<Void> writeStuffCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                byte[] stuff = "Hello World".getBytes();
                file.writeFile(stuff, stuff.length, 0, 0);
                file.close(); // needed to actually flush content to node
                return null;
            }
        };
        tran.doInTransaction(writeStuffCB);

        /**
         * Step 1: Delete file by path
         */
        driver.deleteFile(testSession, testConnection, FILE_PATH);

        /**
         * Step 2: Negative test - Delete file again
         */
        try
        {
            driver.deleteFile(testSession, testConnection, FILE_PATH);
            fail("delete a non existent file");
        }
        catch (IOException fe)
        {
            // expect to go here
        }
    }

    /**
     * Test Set Info
     * 
     * Three flags set
     * <ol>
     * <li>SetDeleteOnClose</li>
     * <li>SetCreationDate</li>
     * <li>SetModifyDate</li>
     * </ol>
     */
    public void testSetFileInfo() throws Exception
    {
        logger.debug("testSetFileInfo");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        Date now = new Date();

        // CREATE 6 hours ago
        final Date CREATED = new Date(now.getTime() - 1000 * 60 * 60 * 6);
        // Modify one hour ago
        final Date MODIFIED = new Date(now.getTime() - 1000 * 60 * 60 * 1);

        class TestContext
        {
            NodeRef testNodeRef;
        }
        ;

        final TestContext testContext = new TestContext();

        /**
         * Step 1 : Create a new file in read/write mode and add some content. Call SetInfo to set the creation date
         */
        int openAction = FileAction.CreateNotExist;

        final String FILE_NAME = "testSetFileInfo.txt";
        final String FILE_PATH = "\\" + FILE_NAME;

        // Clean up junk if it exists
        try
        {
            driver.deleteFile(testSession, testConnection, FILE_PATH);
        }
        catch (IOException ie)
        {
            // expect to go here
        }

        final FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);

        final NetworkFile file = driver.createFile(testSession, testConnection, params);
        assertNotNull("file is null", file);
        assertFalse("file is read only, should be read-write", file.isReadOnly());

        RetryingTransactionCallback<Void> writeStuffCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                byte[] stuff = "Hello World".getBytes();

                driver.writeFile(testSession, testConnection, file, stuff, 0, stuff.length, 0);
                driver.closeFile(testSession, testConnection, file);

                FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
                info.setFileInformationFlags(FileInfo.SetModifyDate);
                info.setModifyDateTime(MODIFIED.getTime());
                driver.setFileInformation(testSession, testConnection, FILE_PATH, info);
                return null;
            }
        };
        tran.doInTransaction(writeStuffCB);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef companyHome = repositoryHelper.getCompanyHome();
                NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
                testContext.testNodeRef = newNode;
                assertNotNull("can't find new node", newNode);
                Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT);
                assertNotNull("content is null", content);
                Date modified = (Date) nodeService.getProperty(newNode, ContentModel.PROP_MODIFIED);
                assertEquals("modified time not set correctly", MODIFIED, modified);
                return null;
            }
        };
        tran.doInTransaction(validateCB);

        /**
         * Step 2: Change the created date
         */
        logger.debug("Step 2: Change the created date");
        RetryingTransactionCallback<Void> changeCreatedCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
                info.setFileInformationFlags(FileInfo.SetCreationDate);
                info.setCreationDateTime(CREATED.getTime());
                driver.setFileInformation(testSession, testConnection, FILE_PATH, info);
                return null;
            }
        };
        tran.doInTransaction(changeCreatedCB);

        RetryingTransactionCallback<Void> validateCreatedCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef companyHome = repositoryHelper.getCompanyHome();
                NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
                testContext.testNodeRef = newNode;
                assertNotNull("can't find new node", newNode);
                Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT);
                assertNotNull("content is null", content);
                Date created = (Date) nodeService.getProperty(newNode, ContentModel.PROP_CREATED);
                assertEquals("created time not set correctly", CREATED, created);
                return null;
            }
        };
        tran.doInTransaction(validateCreatedCB);

        /**
         * Step 3: Test
         */
        logger.debug("Step 3: test deleteOnClose");
        RetryingTransactionCallback<Void> deleteOnCloseCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NetworkFile f2 = driver.openFile(testSession, testConnection, params);

                FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
                info.setFileInformationFlags(FileInfo.SetDeleteOnClose);
                driver.setFileInformation(testSession, testConnection, FILE_PATH, info);
                file.setDeleteOnClose(true);

                byte[] stuff = "Update".getBytes();
                driver.writeFile(testSession, testConnection, file, stuff, 0, stuff.length, 0);
                driver.closeFile(testSession, testConnection, file);

                return null;
            }
        };
        tran.doInTransaction(deleteOnCloseCB);

        RetryingTransactionCallback<Void> validateDeleteOnCloseCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef companyHome = repositoryHelper.getCompanyHome();
                NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
                assertNull("can still find new node", newNode);
                return null;
            }
        };
        tran.doInTransaction(validateDeleteOnCloseCB);

        // clean up so we could run the test again
        // driver.deleteFile(testSession, testConnection, FILE_PATH);

    } // test set file info

    /**
     * Test Open File
     */
    public void testOpenFile() throws Exception
    {
        logger.debug("testOpenFile");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        class TestContext
        {
            NodeRef testDirNodeRef;
        }
        ;

        final TestContext testContext = new TestContext();

        final String FILE_NAME = "testOpenFile.txt";
        FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);
        driver.createDirectory(testSession, testConnection, dirParams);

        testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_ROOT_DOS_PATH);

        /**
         * Step 1 : Negative test - try to open a file that does not exist
         */
        final String FILE_PATH = TEST_ROOT_DOS_PATH + "\\" + FILE_NAME;

        FileOpenParams params = new FileOpenParams(FILE_PATH, FileAction.CreateNotExist, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
        try
        {
            NetworkFile file = driver.openFile(testSession, testConnection, params);
            fail("managed to open non existant file!");
        }
        catch (IOException ie)
        {
            // expect to go here
        }

        /**
         * Step 2: Now create the file through the node service and open it.
         */
        logger.debug("Step 2) Open file created by node service");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                ChildAssociationRef ref = nodeService.createNode(testContext.testDirNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, FILE_NAME), ContentModel.TYPE_CONTENT);
                nodeService.setProperty(ref.getChildRef(), ContentModel.PROP_NAME, FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        NetworkFile file = driver.openFile(testSession, testConnection, params);
        assertNotNull(file);
        assertFalse("file is closed", file.isClosed());

        /**
         * Step 3: Open the root directory.
         */
        logger.debug("Step 3) Open the root directory");

        FileOpenParams rootParams = new FileOpenParams("\\", FileAction.CreateNotExist, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
        NetworkFile file3 = driver.openFile(testSession, testConnection, rootParams);
        assertNotNull(file3);
        assertFalse("file is closed", file3.isClosed());

    } // testOpenFile

    /**
     * Unit test of file exists
     */
    public void testFileExists() throws Exception
    {
        logger.debug("testFileExists");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        final String FILE_PATH = TEST_ROOT_DOS_PATH + "\\testFileExists.new";

        class TestContext
        {}
        ;

        final TestContext testContext = new TestContext();

        /**
         * Step 1 : Call FileExists for a directory which does not exist
         */
        logger.debug("Step 1, negative test dir does not exist");
        int status = driver.fileExists(testSession, testConnection, TEST_ROOT_DOS_PATH);
        assertEquals(status, 0);

        /**
         * Step 2 : Call FileExists for a file which does not exist
         */
        logger.debug("Step 2, negative test file does not exist");
        status = driver.fileExists(testSession, testConnection, FILE_PATH);
        assertEquals(status, 0);

        /**
         * Step 3: Create a new file in read/write mode and add some content.
         */
        int openAction = FileAction.CreateNotExist;

        FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
        FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);

        driver.createDirectory(testSession, testConnection, dirParams);
        final NetworkFile file = driver.createFile(testSession, testConnection, params);
        assertNotNull("file is null", file);
        assertFalse("file is read only, should be read-write", file.isReadOnly());

        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                byte[] stuff = "Hello World".getBytes();
                file.writeFile(stuff, stuff.length, 0, 0);
                driver.closeFile(testSession, testConnection, file);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        status = driver.fileExists(testSession, testConnection, FILE_PATH);
        assertEquals(status, 1);

        /**
         * Step 4 : Delete the node - check status goes back to 0
         */
        logger.debug("Step 4, successfully delete node");
        driver.deleteFile(testSession, testConnection, FILE_PATH);

        status = driver.fileExists(testSession, testConnection, FILE_PATH);
        assertEquals(status, 0);

    } // testFileExists

    /**
     * Unit test of rename file
     */
    public void testRenameFile() throws Exception
    {
        logger.debug("testRenameFile");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);

        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        final String FILE_PATH1 = TEST_ROOT_DOS_PATH + "\\SourceFile1.new";
        final String FILE_NAME2 = "SourceFile2.new";
        final String FILE_PATH2 = TEST_ROOT_DOS_PATH + "\\" + FILE_NAME2;
        final String FILE_PATH3 = TEST_ROOT_DOS_PATH + "\\SourceFile3.new";

        FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);
        driver.createDirectory(testSession, testConnection, dirParams);

        FileOpenParams params1 = new FileOpenParams(FILE_PATH1, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
        final NetworkFile file1 = driver.createFile(testSession, testConnection, params1);

        FileOpenParams params3 = new FileOpenParams(FILE_PATH3, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
        final NetworkFile file3 = driver.createFile(testSession, testConnection, params3);

        /**
         * Step 1 : Negative test, Call Rename for a file which does not exist
         */
        try
        {
            driver.renameFile(testSession, testConnection, "\\Wibble\\wobble", FILE_PATH1);
            fail("rename did not detect missing file");
        }
        catch (IOException e)
        {
            // expect to go here
        }

        /**
         * Step 2: Negative test, Call Rename for a destination that does not exist.
         */
        try
        {
            driver.renameFile(testSession, testConnection, FILE_PATH1, "\\wibble\\wobble");
            fail("rename did not detect missing file");
        }
        catch (IOException e)
        {
            // expect to go here
        }

        /**
         * Step 3: Rename a file to a destination that is a file rather than a directory
         */
        try
        {
            driver.renameFile(testSession, testConnection, FILE_PATH1, FILE_PATH3);
            fail("rename did not detect missing file");
        }
        catch (IOException e)
        {
            // expect to go here
        }

        /**
         * Step 4: Successfully rename a file - check the name, props and content.
         */
        final String LAST_NAME = "Bloggs";

        RetryingTransactionCallback<Void> setPropertiesCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                final NodeRef file1NodeRef = getNodeForPath(testConnection, FILE_PATH1);
                assertNotNull("node ref not found", file1NodeRef);
                nodeService.setProperty(file1NodeRef, ContentModel.PROP_LASTNAME, LAST_NAME);

                return null;
            }
        };
        tran.doInTransaction(setPropertiesCB, false, true);

        driver.renameFile(testSession, testConnection, FILE_PATH1, FILE_PATH2);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef file2NodeRef = getNodeForPath(testConnection, FILE_PATH2);
                // assertEquals("node ref has changed on a rename", file1NodeRef, file2NodeRef);
                assertEquals(nodeService.getProperty(file2NodeRef, ContentModel.PROP_LASTNAME), LAST_NAME);
                ChildAssociationRef parentRef = nodeService.getPrimaryParent(file2NodeRef);
                assertTrue("file has wrong assoc local name", parentRef.getQName().getLocalName().equals(FILE_NAME2));
                assertTrue("not primary assoc", parentRef.isPrimary());

                return null;
            }
        };
        tran.doInTransaction(validateCB, false, true);

        /**
         * Step 5: Rename to another directory
         */
        String DIR_NEW_PATH = TEST_ROOT_DOS_PATH + "\\NewDir";
        final String NEW_PATH = DIR_NEW_PATH + "\\File2";
        FileOpenParams params5 = new FileOpenParams(DIR_NEW_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
        driver.createDirectory(testSession, testConnection, params5);

        final NodeRef newDirNodeRef = getNodeForPath(testConnection, DIR_NEW_PATH);

        driver.renameFile(testSession, testConnection, FILE_PATH2, NEW_PATH);

        RetryingTransactionCallback<Void> validateStep5CB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef file5NodeRef = getNodeForPath(testConnection, NEW_PATH);
                ChildAssociationRef parentRef5 = nodeService.getPrimaryParent(file5NodeRef);

                assertTrue(parentRef5.getParentRef().equals(newDirNodeRef));

                return null;
            }
        };
        tran.doInTransaction(validateStep5CB, false, true);

        // /**
        // * Step 5: rename to self - check no damage.
        // */
        // try
        // {
        // driver.renameFile(testSession, testConnection, FILE_PATH2, FILE_PATH2);
        // fail("rename did not detect rename to self");
        // }
        // catch (IOException e)
        // {
        // expect to go here
        // }

    } // testRenameFile

    /**
     * Unit test of rename versionable file
     */
    public void testScenarioRenameVersionableFile() throws Exception
    {
        logger.debug("testScenarioRenameVersionableFile");

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);

        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        final String FILE_PATH1 = TEST_ROOT_DOS_PATH + "\\SourceFile1.new";
        final String FILE_PATH2 = TEST_ROOT_DOS_PATH + "\\SourceFile2.new";

        class TestContext
        {}
        ;

        final TestContext testContext = new TestContext();

        FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);
        driver.createDirectory(testSession, testConnection, dirParams);

        FileOpenParams params1 = new FileOpenParams(FILE_PATH1, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
        NetworkFile file1 = driver.createFile(testSession, testConnection, params1);

        /**
         * Make Node 1 versionable
         */
        final String LAST_NAME = "Bloggs";

        RetryingTransactionCallback<Void> makeVersionableCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef file1NodeRef = getNodeForPath(testConnection, FILE_PATH1);
                nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                ContentWriter contentWriter2 = contentService.getWriter(file1NodeRef, ContentModel.PROP_CONTENT, true);
                contentWriter2.putContent("test rename versionable");

                nodeService.setProperty(file1NodeRef, ContentModel.PROP_LASTNAME, LAST_NAME);
                nodeService.setProperty(file1NodeRef, TransferModel.PROP_ENDPOINT_PROTOCOL, "http");

                return null;
            }
        };
        tran.doInTransaction(makeVersionableCB, false, true);

        /**
         * Step 1: Successfully rename a versionable file - check the name, props and content. TODO Check primary assoc, peer assocs, child assocs, modified date, created date, nodeid, permissions.
         */
        driver.renameFile(testSession, testConnection, FILE_PATH1, FILE_PATH2);

        RetryingTransactionCallback<Void> validateVersionableCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef file2NodeRef = getNodeForPath(testConnection, FILE_PATH2);
                assertNotNull("file2 node ref is null", file2NodeRef);
                // assertEquals(nodeService.getProperty(file2NodeRef, ContentModel.PROP_LASTNAME), LAST_NAME);
                assertTrue("does not have versionable aspect", nodeService.hasAspect(file2NodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertTrue("sample property is null", nodeService.getProperty(file2NodeRef, TransferModel.PROP_ENDPOINT_PROTOCOL) != null);

                return null;
            }
        };
        tran.doInTransaction(validateVersionableCB, false, true);

    } // testRenameVersionable

    /**
     * This test tries to simulate the shuffling that is done by MS Word 2003 upon file save
     * 
     * a) TEST.DOC b) Save to ~WRDnnnn.TMP c) Delete ~WRLnnnn.TMP d) Rename TEST.DOC ~WDLnnnn.TMP e) Delete TEST.DOC f) Rename ~WRDnnnn.TMP to TEST.DOC g) Delete ~WRLnnnn.TMP
     * 
     * We need to check that properties, aspects, primary assocs, secondary assocs, peer assocs, node type, version history, creation date are maintained.
     */
    public void testScenarioMSWord2003SaveShuffle() throws Exception
    {
        logger.debug("testScenarioMSWord2003SaveShuffle");
        final String FILE_NAME = "TEST.DOC";
        final String FILE_TITLE = "Test document";
        final String FILE_DESCRIPTION = "This is a test document to test CIFS shuffle";
        final String FILE_OLD_TEMP = "~WRL0002.TMP";
        final String FILE_NEW_TEMP = "~WRD0002.TMP";

        final QName RESIDUAL_MTTEXT = QName.createQName("{gsxhjsx}", "whatever");

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;
            NetworkFile oldFileHandle;

            NodeRef testNodeRef; // node ref of test.doc

            Serializable testCreatedDate;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testScenarioMSWord2003SaveShuffle";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);

        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        /**
         * Create a file in the test directory
         */

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // now load up the node with lots of other stuff that we will test to see if it gets preserved during the
                // shuffle.
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                // test non CM namespace property
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
                // test CM property not related to an aspect
                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_ADDRESSEE, "Fred");

                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_TITLE, FILE_TITLE);
                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_DESCRIPTION, FILE_DESCRIPTION);

                /**
                 * MLText value - also a residual value in a non cm namespace
                 */
                MLText mltext = new MLText();
                mltext.addValue(Locale.FRENCH, "Bonjour");
                mltext.addValue(Locale.ENGLISH, "Hello");
                mltext.addValue(Locale.ITALY, "Buongiorno");
                mlAwareNodeService.setProperty(testContext.testNodeRef, RESIDUAL_MTTEXT, mltext);

                // classifiable chosen since its not related to any properties.
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_CLASSIFIABLE, null);
                // nodeService.createAssociation(testContext.testNodeRef, targetRef, assocTypeQName);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * Write some content to the test file
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                String testContent = "MS Word 2003 shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.firstFileHandle.close();

                testContext.testCreatedDate = nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_CREATED);

                MLText multi = (MLText) mlAwareNodeService.getProperty(testContext.testNodeRef, RESIDUAL_MTTEXT);
                multi.getValues();

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * b) Save the new file
         */
        RetryingTransactionCallback<Void> saveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);
                String testContent = "MS Word 2003 shuffle test This is new content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.newFileHandle.close();

                return null;
            }
        };
        tran.doInTransaction(saveNewFileCB, false, true);

        /**
         * rename the old file
         */
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        RetryingTransactionCallback<Void> validateOldFileGoneCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                try
                {
                    driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                }
                catch (IOException e)
                {
                    // expect to go here since previous step renamed the file.
                }

                return null;
            }
        };
        tran.doInTransaction(validateOldFileGoneCB, false, true);

        logger.debug("Shuffle step next");
        /**
         * Move the new file into place, stuff should get shuffled
         */
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);
        logger.debug("end of shuffle step");

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                // Check trx:enabled has been shuffled.
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
                // check my residual MLText has been transferred
                assertTrue(props.containsKey(RESIDUAL_MTTEXT));

                // Check the titled aspect is correct
                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));
                assertEquals("title wrong", FILE_TITLE, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_TITLE));
                assertEquals("description wrong", FILE_DESCRIPTION, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_DESCRIPTION));

                // ALF-7641 - CIFS shuffle, does not preseve MLText values.
                Map<QName, Serializable> mlProps = mlAwareNodeService.getProperties(shuffledNodeRef);

                MLText multi = (MLText) mlAwareNodeService.getProperty(shuffledNodeRef, RESIDUAL_MTTEXT);
                assertTrue("MLText has lost values", multi.getValues().size() > 2);

                // // ALF-7635 check auditable properties
                assertEquals("creation date not preserved", ((java.util.Date) testContext.testCreatedDate).getTime(), ((java.util.Date) nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_CREATED)).getTime());

                // ALF-7628 - preserve addressee and classifiable
                assertEquals("ADDRESSEE PROPERTY Not copied", "Fred", nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_ADDRESSEE));
                assertTrue("CLASSIFIABLE aspect not present", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_CLASSIFIABLE));

                // ALF-7584 - preserve node ref.
                assertEquals("noderef changed", testContext.testNodeRef, shuffledNodeRef);
                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };

        tran.doInTransaction(deleteOldFileCB, false, true);

    } // testScenarioMSWord2003SaveShuffle

    /**
     * This test tries to simulate the shuffling that is done by MS Word 2003 with backup enabled upon file save
     * 
     * a) TEST.DOC b) Save to ~WRDnnnn.TMP c) Delete "Backup of TEST.DOC" d) Rename TEST.DOC to "Backup of TEST.DOC" e) Delete TEST.DOC f) Rename ~WRDnnnn.TMP to TEST.DOC
     * 
     * We need to check that properties, aspects, primary assocs, secondary assocs, peer assocs, node type, version history, creation date are maintained.
     */
    public void testScenarioMSWord2003SaveShuffleWithBackup() throws Exception
    {
        logger.debug("testScenarioMSWord2003SaveShuffleWithBackup");
        final String FILE_NAME = "TEST.DOC";
        final String FILE_OLD_TEMP = "Backup of TEST.DOC";
        final String FILE_NEW_TEMP = "~WRD0002.TMP";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;
            NodeRef testNodeRef; // node ref of test.doc
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioMSWord2003SaveShuffleWithBackup";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // now load up the node with lots of other stuff that we will test to see if it gets preserved during the
                // shuffle.
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                // test non CM namespace property
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
                // test CM property not related to an aspect
                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_ADDRESSEE, "Fred");
                nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_CREATED);
                // classifiable chosen since its not related to any properties.
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_CLASSIFIABLE, null);
                // nodeService.createAssociation(testContext.testNodeRef, targetRef, assocTypeQName);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * Write some content to the test file
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                String testContent = "MS Word 2003 shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.firstFileHandle.close();
                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * b) Save the new file
         */
        RetryingTransactionCallback<Void> saveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);
                String testContent = "MS Word 2003 shuffle test This is new content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.newFileHandle.close();

                return null;
            }
        };
        tran.doInTransaction(saveNewFileCB, false, true);

        /**
         * rename the old file
         */
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        RetryingTransactionCallback<Void> validateOldFileGoneCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                try
                {
                    driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                }
                catch (IOException e)
                {
                    // expect to go here since previous step renamed the file.
                }

                return null;
            }
        };
        tran.doInTransaction(validateOldFileGoneCB, false, true);

        /**
         * Move the new file into place, stuff should get shuffled
         */
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));

                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));

                // commented out due to ALF-7628
                // assertEquals("ADDRESSEE PROPERTY Not copied", "Fred", nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_ADDRESSEE));
                // assertEquals("created date changed", testContext.testCreatedDate, (Date)nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_CREATED));

                // assertTrue("CLASSIFIABLE aspect not present", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_CLASSIFIABLE));

                // assertEquals("noderef changed", testContext.testNodeRef, shuffledNodeRef);
                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

    } // testScenarioMSWord2003SaveShuffleWithBackup

    /**
     * This test tries to simulate the cifs shuffling that is done to support MS Word 2007
     * 
     * a) TEST.DOCX b) Save new to 00000001.TMP c) Rename TEST.DOCX to 00000002.TMP d) Rename 000000001.TMP to TEST.DOCX e) Delete 000000002.TMP
     */
    public void testScenarioMSWord2007Save() throws Exception
    {
        logger.debug("testScenarioMSWord2007SaveShuffle");
        final String FILE_NAME = "TEST.DOCX";
        final String FILE_OLD_TEMP = "788A1D3D.tmp";
        final String FILE_NEW_TEMP = "19ECA1A.tmp";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;
            NodeRef testNodeRef; // node ref of test.doc
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioMSWord2007Save";
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // no need to test lots of different properties, that's already been tested above
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * a) Write some content to the test file
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                String testContent = "MS Word 2007 shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.firstFileHandle.close();
                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * b) Save the new file
         */
        RetryingTransactionCallback<Void> saveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);
                String testContent = "MS Word 2007 shuffle test This is new content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.newFileHandle.close();

                return null;
            }
        };
        tran.doInTransaction(saveNewFileCB, false, true);

        /**
         * c) rename the old file
         */
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * d) Move the new file into place, stuff should get shuffled
         */
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        RetryingTransactionCallback<Void> deleteOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(deleteOldFileCB, false, true);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));
                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

    } // testScenarioWord2007 save

    /**
     * This test tries to simulate the cifs shuffling that is done to support EMACS
     * 
     * a) emacsTest.txt b) Rename original file to emacsTest.txt~ c) Create emacsTest.txt
     */
    public void DISABLED_testScenarioEmacsSave() throws Exception
    {
        logger.debug("testScenarioEmacsSave");
        final String FILE_NAME = "emacsTest.txt";
        final String FILE_OLD_TEMP = "emacsTest.txt~";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;
            NodeRef testNodeRef; // node ref of test.doc
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioEmacsSave";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // no need to test lots of different properties, that's already been tested above
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);

                return null;
            }
        };
        tran.doInTransaction(createFileCB);

        /**
         * a) Write some content to the test file
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                String testContent = "Emacs shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.firstFileHandle.close();
                return null;
            }
        };
        tran.doInTransaction(writeFileCB);

        /**
         * b) rename the old file out of the way
         */
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB);

        /**
         * c) Save the new file
         */
        RetryingTransactionCallback<Void> saveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);
                String testContent = "EMACS shuffle test This is new content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.newFileHandle.close();

                return null;
            }
        };
        tran.doInTransaction(saveNewFileCB);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
                return null;
            }
        };

        tran.doInTransaction(validateCB);

    } // testScenarioEmacs save

    /**
     * This test tries to simulate the cifs shuffling that is done to support vi
     * 
     * a) viTest.txt b) Rename original file to viTest.txt~ c) Create viTest.txt d) Delete viTest.txt~
     */
    public void testScenarioViSave() throws Exception
    {
        logger.debug("testScenarioViSave");
        final String FILE_NAME = "viTest.txt";
        final String FILE_OLD_TEMP = "viTest.txt~";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;
            NodeRef testNodeRef; // node ref of test.doc
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioViSave";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // no need to test lots of different properties, that's already been tested above
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);

                return null;
            }
        };
        tran.doInTransaction(createFileCB);

        /**
         * a) Write some content to the test file
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                String testContent = "Emacs shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
                return null;
            }
        };
        tran.doInTransaction(writeFileCB);

        /**
         * b) rename the old file out of the way
         */
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB);

        /**
         * c) Save the new file
         */
        RetryingTransactionCallback<Void> saveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);
                String testContent = "Vi shuffle test This is new content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                driver.closeFile(testSession, testConnection, testContext.newFileHandle);
                logger.debug("delete temporary file - which will trigger shuffle");
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(saveNewFileCB);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertNotNull("shuffledNodeRef is null", shuffledNodeRef);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);
                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));

                return null;
            }
        };

        tran.doInTransaction(validateCB);

    } // testScenarioViSave

    /**
     * This test tries to simulate the cifs shuffling that is done to support smultron
     * 
     * a) smultronTest.txt b) Save new file to .dat04cd.004 c) Delete smultronTest.txt c) Rename .dat04cd.004 to smultronTest.txt
     */
    public void testScenarioSmultronSave() throws Exception
    {
        logger.debug("testScenarioSmultronSave");
        final String FILE_NAME = "smultronTest.txt";
        final String FILE_NEW_TEMP = ".dat04cd.004";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;
            NodeRef testNodeRef; // node ref of test.doc
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioSmultronSave";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // no need to test lots of different properties, that's already been tested above
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);

                return null;
            }
        };
        tran.doInTransaction(createFileCB);

        /**
         * a) Write some content to the test file
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                String testContent = "Smultron shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
                return null;
            }
        };
        tran.doInTransaction(writeFileCB);

        /**
         * b) Save the new file
         */
        RetryingTransactionCallback<Void> saveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);
                String testContent = "Smultron shuffle test This is new content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                driver.closeFile(testSession, testConnection, testContext.newFileHandle);

                return null;
            }
        };
        tran.doInTransaction(saveNewFileCB);

        /**
         * c) Delete the old file
         */
        RetryingTransactionCallback<Void> deleteOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(deleteOldFileCB);

        /**
         * d) Move the new file into place, stuff should get shuffled
         */
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));
                return null;
            }
        };

        tran.doInTransaction(validateCB);

    } // testScenarioSmultronSave

    /**
     * This time we create a file through the ContentDiskDriver and then delete it through the repo. We check its no longer found by the driver.
     */
    public void testScenarioDeleteViaNodeService() throws Exception
    {
        logger.debug("testScenarioDeleteViaNodeService");

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        int openAction = FileAction.CreateNotExist;
        final String FILE_NAME = "testDeleteFileViaNodeService.new";
        final String FILE_PATH = "\\" + FILE_NAME;

        FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);

        final NetworkFile file = driver.createFile(testSession, testConnection, params);

        assertNotNull("file is null", file);
        assertFalse("file is read only, should be read-write", file.isReadOnly());

        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                byte[] stuff = "Hello World".getBytes();
                file.writeFile(stuff, stuff.length, 0, 0);
                file.close();

                NodeRef companyHome = repositoryHelper.getCompanyHome();
                NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
                assertNotNull("can't find new node", newNode);

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * Step 1: Delete the new node via the node service
         */
        RetryingTransactionCallback<Void> deleteNodeCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                NodeRef companyHome = repositoryHelper.getCompanyHome();
                NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
                assertNotNull("can't find new node", newNode);
                nodeService.deleteNode(newNode);
                return null;
            }
        };
        tran.doInTransaction(deleteNodeCB, false, true);

        try
        {
            getNodeForPath(testConnection, FILE_PATH);
            fail("getNode for path unexpectedly succeeded");
        }
        catch (IOException ie)
        {
            // expect to go here
        }

        /**
         * Delete file by path - file should no longer exist
         */
        try
        {
            driver.deleteFile(testSession, testConnection, FILE_PATH);
            fail("delete unexpectedly succeeded");
        }
        catch (IOException ie)
        {
            // expect to go here
        }

    }

    /**
     * This test tries to simulate the shuffling that is done by MS Word 2003 with regard to metadata extraction.
     * <p>
     * 1: Setup an inbound rule for ContentMetadataExtractor. 2: Write ContentDiskDriverTest1 file to ContentDiskDriver.docx 3: Check metadata extraction for non update test Simulate a WORD 2003 CIFS shuffle 4: Write ContentDiskDriverTest2 file to ~WRD0003.TMP 5: Rename ContentDiskDriver.docx to ~WRL0003.TMP 6: Rename ~WRD0003.TMP to ContentDiskDriver.docx 7: Check metadata extraction
     */
    public void testMetadataExtraction() throws Exception
    {
        logger.debug("testMetadataExtraction");
        final String FILE_NAME = "ContentDiskDriver.docx";
        final String FILE_OLD_TEMP = "~WRL0003.TMP";
        final String FILE_NEW_TEMP = "~WRD0003.TMP";

        class TestContext
        {
            NodeRef testDirNodeRef;
            NodeRef testNodeRef;
            NetworkFile firstFileHandle;
            NetworkFile secondFileHandle;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testMetadataExtraction";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageDirCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteDirectory(testSession, testConnection, TEST_DIR);
                return null;
            }
        };

        try
        {
            transactionService.getRetryingTransactionHelper().doInTransaction(deleteGarbageDirCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("create Test directory" + TEST_DIR);
        RetryingTransactionCallback<Void> createTestDirCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_DIR);
                assertNotNull("testDirNodeRef is null", testContext.testDirNodeRef);

                UserTransaction txn = transactionService.getUserTransaction();

                return null;

            }
        };
        transactionService.getRetryingTransactionHelper().doInTransaction(createTestDirCB);
        logger.debug("Create rule on test dir");

        RetryingTransactionCallback<Void> createRuleCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                Rule rule = new Rule();
                rule.setRuleType(RuleType.INBOUND);
                rule.applyToChildren(true);
                rule.setRuleDisabled(false);
                rule.setTitle("Extract Metadata from content");
                rule.setDescription("ContentDiskDriverTest");

                Map<String, Serializable> props = new HashMap<String, Serializable>(1);
                Action extractAction = actionService.createAction("extract-metadata", props);

                ActionCondition noCondition1 = actionService.createActionCondition(NoConditionEvaluator.NAME);
                extractAction.addActionCondition(noCondition1);

                ActionCondition noCondition2 = actionService.createActionCondition(NoConditionEvaluator.NAME);
                CompositeAction compAction = actionService.createCompositeAction();
                compAction.setTitle("Extract Metadata");
                compAction.setDescription("Content Disk Driver Test - Extract Metadata");
                compAction.addAction(extractAction);
                compAction.addActionCondition(noCondition2);

                rule.setAction(compAction);

                ruleService.saveRule(testContext.testDirNodeRef, rule);

                logger.debug("rule created");

                return null;
            }
        };
        transactionService.getRetryingTransactionHelper().doInTransaction(createRuleCB, false, true);

        /**
         * Create a file in the test directory
         */
        logger.debug("create test file in test directory");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the file we are going to use to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // now load up the node with lots of other stuff that we will test to see if it gets preserved during the
                // shuffle.
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertNotNull("testContext.testNodeRef is null", testContext.testNodeRef);

                // test non CM namespace property
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);

                return null;
            }
        };
        transactionService.getRetryingTransactionHelper().doInTransaction(createFileCB, false, true);

        logger.debug("step b: write content to test file");

        /**
         * Write ContentDiskDriverTest1.docx to the test file,
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest1.docx");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest1.docx", fileResource);
                writeResourceToNetworkFile(fileResource, testContext.firstFileHandle);

                logger.debug("close the file, firstFileHandle");
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                return null;
            }
        };
        transactionService.getRetryingTransactionHelper().doInTransaction(writeFileCB, false, true);

        Thread.sleep(3000); // Need to wait for the async extract
        logger.debug("Step c: validate metadata has been extracted.");

        /**
         * c: check simple case of meta-data extraction has worked.
         */
        transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>() {
            public Void execute() throws Throwable
            {
                Map<QName, Serializable> props = nodeService.getProperties(testContext.testNodeRef);

                assertTrue("Enabled property has been lost", props.containsKey(TransferModel.PROP_ENABLED));

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertEquals("size is wrong", 11302, data.getSize());
                assertEquals("mimeType is wrong", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", data.getMimetype());

                // These metadata values should be extracted.
                assertEquals("description is not correct", "This is a test file", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_DESCRIPTION));
                assertEquals("title is not correct", "ContentDiskDriverTest", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_TITLE));
                assertEquals("author is not correct", "mrogers", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_AUTHOR));

                return null;
            }
        });

        /**
         * d: Save the new file as an update file in the test directory
         */
        logger.debug("Step d: create update file in test directory " + FILE_NEW_TEMP);
        RetryingTransactionCallback<Void> createUpdateFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the file we are going to use to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.secondFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.secondFileHandle);

                return null;
            }
        };
        transactionService.getRetryingTransactionHelper().doInTransaction(createUpdateFileCB, false, true);

        RetryingTransactionCallback<Void> writeFile2CB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest2.docx");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest2.docx", fileResource);

                byte[] buffer = new byte[1000];
                InputStream is = fileResource.getInputStream();
                try
                {
                    long offset = 0;
                    int i = is.read(buffer, 0, buffer.length);
                    while (i > 0)
                    {
                        testContext.secondFileHandle.writeFile(buffer, i, 0, offset);
                        offset += i;
                        i = is.read(buffer, 0, buffer.length);
                    }
                }
                finally
                {
                    is.close();
                }

                driver.closeFile(testSession, testConnection, testContext.secondFileHandle);

                return null;
            }
        };
        transactionService.getRetryingTransactionHelper().doInTransaction(writeFile2CB, false, true);

        /**
         * rename the old file
         */
        logger.debug("move old file out of the way.");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        transactionService.getRetryingTransactionHelper().doInTransaction(renameOldFileCB, false, true);

        /**
         * Check the old file has gone.
         */
        RetryingTransactionCallback<Void> validateOldFileGoneCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                try
                {
                    driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                }
                catch (IOException e)
                {
                    // expect to go here since previous step renamed the file.
                }

                return null;
            }
        };
        transactionService.getRetryingTransactionHelper().doInTransaction(validateOldFileGoneCB, false, true);

        // /**
        // * Check metadata extraction on intermediate new file
        // */
        // RetryingTransactionCallback<Void> validateIntermediateCB = new RetryingTransactionCallback<Void>() {
        //
        // @Override
        // public Void execute() throws Throwable
        // {
        // NodeRef updateNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP);
        //
        // Map<QName, Serializable> props = nodeService.getProperties(updateNodeRef);
        //
        // // These metadata values should be extracted from file2.
        // assertEquals("intermediate file description is not correct", "Content Disk Test 2", props.get(ContentModel.PROP_DESCRIPTION));
        // assertEquals("intermediate file title is not correct", "Updated", props.get(ContentModel.PROP_TITLE));
        // assertEquals("intermediate file author is not correct", "mrogers", props.get(ContentModel.PROP_AUTHOR));
        //
        // return null;
        // }
        // };
        //
        // transactionService.getRetryingTransactionHelper().doInTransaction(validateIntermediateCB, true, true);

        /**
         * Move the new file into place, stuff should get shuffled
         */
        logger.debug("move new file into place.");
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        transactionService.getRetryingTransactionHelper().doInTransaction(moveNewFileCB, false, true);

        logger.debug("validate update has run correctly.");
        RetryingTransactionCallback<Void> validateUpdateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                // Check trx:enabled has been shuffled and not lost.
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertEquals("mimeType is wrong", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", data.getMimetype());
                assertEquals("size is wrong", 11265, data.getSize());

                // These metadata values should be extracted from file2. However they will not be applied in PRAGMATIC mode.
                // assertEquals("description is not correct", "Content Disk Test 2", props.get(ContentModel.PROP_DESCRIPTION));
                // assertEquals("title is not correct", "Updated", props.get(ContentModel.PROP_TITLE));
                // assertEquals("author is not correct", "mrogers", props.get(ContentModel.PROP_AUTHOR));

                return null;
            }
        };

        transactionService.getRetryingTransactionHelper().doInTransaction(validateUpdateCB, true, true);

    } // testScenarioShuffleMetadataExtraction

    /**
     * ALF-12812
     * 
     * This test tries to simulate the shuffling that is done by MS Word 2011 for Mac with regard to metadata extraction. In particular the temporary file names are different.
     * <p>
     * 1: Setup an update rule for ContentMetadataExtractor. Simulate a WORD 2011 for Mac Create 2: Write "Word Work File D_1725484373.tmp" 3: Close file 4: Rename "Word Work File D_1725484373.tmp" to ContentDiskDriver.docx 5: Check metadata extraction
     */
    public void testMetadataExtractionForMac() throws Exception
    {
        logger.debug("testMetadataExtractionForMac");
        final String FILE_NAME = "ContentDiskDriver.docx";
        // final String FILE_OLD_TEMP = "._Word Work File D_1725484373.tmp";
        final String FILE_NEW_TEMP = "Word Work File D_1725484373.tmp";

        class TestContext
        {
            NodeRef testDirNodeRef;
            NodeRef testNodeRef;
            NetworkFile firstFileHandle;
            // NetworkFile secondFileHandle;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testMetadataExtractionForMac";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "cifs", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageDirCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteDirectory(testSession, testConnection, TEST_DIR);
                return null;
            }
        };

        try
        {
            tran.doInTransaction(deleteGarbageDirCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("create Test directory" + TEST_DIR);
        RetryingTransactionCallback<Void> createTestDirCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_DIR);
                assertNotNull("testDirNodeRef is null", testContext.testDirNodeRef);

                UserTransaction txn = transactionService.getUserTransaction();

                return null;

            }
        };
        tran.doInTransaction(createTestDirCB);
        logger.debug("Create rule on test dir");

        RetryingTransactionCallback<Void> createRuleCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                Rule rule = new Rule();
                rule.setRuleType(RuleType.UPDATE);
                rule.applyToChildren(true);
                rule.setRuleDisabled(false);
                rule.setTitle("Extract Metadata from update content");
                rule.setDescription("ContentDiskDriverTest");

                Map<String, Serializable> props = new HashMap<String, Serializable>(1);
                Action extractAction = actionService.createAction("extract-metadata", props);

                ActionCondition noCondition1 = actionService.createActionCondition(NoConditionEvaluator.NAME);
                extractAction.addActionCondition(noCondition1);

                ActionCondition noCondition2 = actionService.createActionCondition(NoConditionEvaluator.NAME);
                CompositeAction compAction = actionService.createCompositeAction();
                compAction.setTitle("Extract Metadata");
                compAction.setDescription("Content Disk Driver Test - Extract Metadata");
                compAction.addAction(extractAction);
                compAction.addActionCondition(noCondition2);

                rule.setAction(compAction);

                ruleService.saveRule(testContext.testDirNodeRef, rule);

                logger.debug("rule created");

                return null;
            }
        };
        tran.doInTransaction(createRuleCB, false, true);

        /**
         * Create a file in the test directory
         */
        logger.debug("create test file in test directory");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the file we are going to use to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull("first file Handle is null", testContext.firstFileHandle);

                // now load up the node with lots of other stuff that we will test to see if it gets preserved during the
                // shuffle.
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP);
                assertNotNull("testContext.testNodeRef is null", testContext.testNodeRef);

                // test non CM namespace property
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);

                // Check that the temporary aspect has been applied.
                assertTrue("temporary aspect not applied", nodeService.hasAspect(testContext.testNodeRef, ContentModel.ASPECT_TEMPORARY));
                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        logger.debug("step b: write content to test file");

        /**
         * Write ContentDiskDriverTest1.docx to the test file,
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest1.docx");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest1.docx", fileResource);

                byte[] buffer = new byte[1000];
                InputStream is = fileResource.getInputStream();
                try
                {
                    long offset = 0;
                    int i = is.read(buffer, 0, buffer.length);
                    while (i > 0)
                    {
                        testContext.firstFileHandle.writeFile(buffer, i, 0, offset);
                        offset += i;
                        i = is.read(buffer, 0, buffer.length);
                    }
                }
                finally
                {
                    is.close();
                }

                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        logger.debug("Step b: rename the test file.");

        /**
         * Move the new file into place, stuff should get shuffled
         */
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        Thread.sleep(3000); // Need to wait for async extract

        logger.debug("Step c: validate metadata has been extracted.");
        /**
         * c: check simple case of meta-data extraction has worked.
         */
        RetryingTransactionCallback<Void> validateFirstExtractionCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                Map<QName, Serializable> props = nodeService.getProperties(testContext.testNodeRef);

                assertTrue("Enabled property has been lost", props.containsKey(TransferModel.PROP_ENABLED));

                // Check that the temporary aspect has been applied.
                assertTrue("temporary aspect has not been removed", !nodeService.hasAspect(testContext.testNodeRef, ContentModel.ASPECT_TEMPORARY));
                assertTrue("hidden aspect has not been removed", !nodeService.hasAspect(testContext.testNodeRef, ContentModel.ASPECT_HIDDEN));

                // These metadata values should be extracted.
                assertEquals("description is not correct", "This is a test file", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_DESCRIPTION));
                assertEquals("title is not correct", "ContentDiskDriverTest", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_TITLE));
                assertEquals("author is not correct", "mrogers", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_AUTHOR));

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertEquals("mimeType is wrong", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", data.getMimetype());
                assertEquals("size is wrong", 11302, data.getSize());

                return null;
            }
        };
        tran.doInTransaction(validateFirstExtractionCB, false, true);

    } // testScenarioMetadataExtractionForMac

    public void testDirListing() throws Exception
    {
        logger.debug("testDirListing");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        final String FOLDER_NAME = "parentFolder" + System.currentTimeMillis();
        final String HIDDEN_FOLDER_NAME = "hiddenFolder" + System.currentTimeMillis();
        RetryingTransactionCallback<NodeRef> createNodesCB = new RetryingTransactionCallback<NodeRef>() {

            @Override
            public NodeRef execute() throws Throwable
            {
                NodeRef companyHome = repositoryHelper.getCompanyHome();
                NodeRef parentNode = nodeService.createNode(companyHome, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, FOLDER_NAME), ContentModel.TYPE_FOLDER).getChildRef();
                nodeService.setProperty(parentNode, ContentModel.PROP_NAME, FOLDER_NAME);

                NodeRef hiddenNode = nodeService.createNode(parentNode, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, HIDDEN_FOLDER_NAME), ForumModel.TYPE_FORUM).getChildRef();
                nodeService.setProperty(hiddenNode, ContentModel.PROP_NAME, HIDDEN_FOLDER_NAME);
                return parentNode;
            }
        };
        final NodeRef parentFolder = tran.doInTransaction(createNodesCB);

        List<String> excludedTypes = new ArrayList<String>();
        excludedTypes.add(ForumModel.TYPE_FORUM.toString());
        cifsHelper.setExcludedTypes(excludedTypes);
        SearchContext result = driver.startSearch(testSession, testConnection, "\\" + FOLDER_NAME + "\\*", 0);
        while (result.hasMoreFiles())
        {
            if (result.nextFileName().equals(HIDDEN_FOLDER_NAME))
            {
                fail("Exluded types mustn't be shown in cifs");
            }
        }

        RetryingTransactionCallback<Void> deleteNodeCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                nodeService.deleteNode(parentFolder);
                return null;
            }
        };
        tran.doInTransaction(deleteNodeCB, false, true);
    } // testDirListing

    public void testFileInformationUpdatingByEditorUserForAlf8808() throws Exception
    {
        final org.alfresco.service.cmr.model.FileInfo[] editorFolder = new org.alfresco.service.cmr.model.FileInfo[]{null};
        final org.alfresco.service.cmr.model.FileInfo[] testFile = new org.alfresco.service.cmr.model.FileInfo[]{null};

        // Configuring test server with test server configuration and getting test tree connection for test shared device
        ServerConfiguration config = new ServerConfiguration(ContentDiskDriverTest.TEST_SERVER_NAME);
        TestServer server = new TestServer(ContentDiskDriverTest.TEST_SERVER_NAME, config);
        DiskSharedDevice device = getDiskSharedDevice();
        final TreeConnection treeConnection = server.getTreeConnection(device);

        // Getting target entity for testing - ContentDiskDriver
        final ExtendedDiskInterface deviceInterface = (ExtendedDiskInterface) treeConnection.getInterface();
        // Creating mock-session
        final SrvSession session = new TestSrvSession(13, server, ContentDiskDriverTest.TEST_PROTOTYPE_NAME, ContentDiskDriverTest.TEST_REMOTE_NAME);

        transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable
            {
                try
                {
                    NodeRef rootNode = repositoryHelper.getCompanyHome();
                    // Creating test user to invite him as Editor for test content. This user will be created correctly (with person and authentication options)
                    createUser(ContentDiskDriverTest.TEST_USER_AUTHORITY, ContentDiskDriverTest.TEST_USER_AUTHORITY, rootNode);
                    // Safely creating folder for test content
                    editorFolder[0] = getOrCreateNode(rootNode, PermissionService.EDITOR, ContentModel.TYPE_FOLDER).getFirst();
                    // Creating test content which will be editable by user created above
                    testFile[0] = getOrCreateNode(rootNode, "Test.txt", ContentModel.TYPE_CONTENT).getFirst();

                    // Applying 'Editor' role for test user to test file
                    permissionService.setPermission(testFile[0].getNodeRef(), ContentDiskDriverTest.TEST_USER_AUTHORITY, PermissionService.EDITOR, true);

                    try
                    {
                        // Creating data for target method invocation
                        final FileInfo updatedInfo = new FileInfo();
                        updatedInfo.setFileName(testFile[0].getName());
                        updatedInfo.setFileId(DefaultTypeConverter.INSTANCE.intValue(testFile[0].getProperties().get(ContentModel.PROP_NODE_DBID)));

                        // Testing ContentDiskDriver.setFileInformation() with test user authenticated who has 'Editor' role for test content.
                        // This method should fail if check on 'DELETE' permission was not moved to 'DeleteOnClose' context
                        AuthenticationUtil.runAs(new RunAsWork<Void>() {
                            @Override
                            public Void doWork() throws Exception
                            {
                                deviceInterface.setFileInformation(session, treeConnection, testFile[0].getName(), updatedInfo);
                                return null;
                            }
                        }, ContentDiskDriverTest.TEST_USER_AUTHORITY);
                    }
                    catch (Exception e)
                    {
                        // Informing about test failure. Expected exception is 'org.alfresco.jlan.server.filesys.AccessDeniedException'
                        if (e.getCause() instanceof AccessDeniedException)
                        {
                            fail("For user='" + TEST_USER_AUTHORITY + "' " + e.getCause().toString());
                        }
                        else
                        {
                            fail("Unexpected exception was caught: " + e.toString());
                        }
                    }
                }
                finally
                {
                    // Cleaning all test data and rolling back transaction to revert all introduced changes during testing

                    if (authenticationService.authenticationExists(ContentDiskDriverTest.TEST_USER_AUTHORITY))
                    {
                        authenticationService.deleteAuthentication(ContentDiskDriverTest.TEST_USER_AUTHORITY);
                    }

                    if (personService.personExists(ContentDiskDriverTest.TEST_USER_AUTHORITY))
                    {
                        personService.deletePerson(ContentDiskDriverTest.TEST_USER_AUTHORITY);
                    }

                    try
                    {
                        if (null != testFile[0])
                        {
                            nodeService.deleteNode(testFile[0].getNodeRef());
                        }
                    }
                    catch (Exception e)
                    {
                        // Doing nothing
                    }

                    try
                    {
                        if (null != editorFolder[0])
                        {
                            nodeService.deleteNode(editorFolder[0].getNodeRef());
                        }
                    }
                    catch (Exception e)
                    {
                        // Doing nothing
                    }
                }

                return null;
            }
        }, false, true);
    }

    /**
     * Searching for file object with specified name or creating new one if such object is not exist
     * 
     * @param parentRef
     *            - {@link NodeRef} of desired parent object
     * @param name
     *            - {@link String} value for name of desired file object
     * @param type
     *            - {@link QName} instance which determines type of the object. It may be cm:content, cm:folder etc (see {@link ContentModel})
     * @return {@link Pair}&lt;{@link org.alfresco.service.cmr.model.FileInfo}, {@link Boolean}> instance which contains {@link NodeRef} of newly created object and <code>true</code> value if file object with specified name was not found or {@link NodeRef} of existent file object and <code>false</code> in other case
     */
    private Pair<org.alfresco.service.cmr.model.FileInfo, Boolean> getOrCreateNode(NodeRef parentRef, String name, QName type)
    {
        NodeRef result = nodeService.getChildByName(parentRef, ContentModel.ASSOC_CONTAINS, name);
        Boolean created = false;
        if (null == result)
        {
            result = nodeService.getChildByName(parentRef, ContentModel.ASSOC_CHILDREN, name);
        }
        if (created = (null == result))
        {
            result = fileFolderService.create(parentRef, name, type).getNodeRef();
        }
        return new Pair<org.alfresco.service.cmr.model.FileInfo, Boolean>(fileFolderService.getFileInfo(result), created);
    }

    /**
     * Creates correct user entity with correct user home space, person and authentication with password equal to '<code>password</code>' options if these options are not exist. Method searches for space with name equal to '<code>name</code>' to make it user home space or creates new folder with name equal to '<code>name</code>'. All required permissions and roles will be applied to user home space
     * 
     * @param name
     *            - {@link String} value which contains new user name
     * @param password
     *            - {@link String} value of text password for new user
     * @param parentNodeRef
     *            - {@link NodeRef} instance of parent folder where user home space should be found or created
     */
    private void createUser(String name, String password, NodeRef parentNodeRef)
    {
        Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
        properties.put(ContentModel.PROP_USERNAME, name);
        Pair<org.alfresco.service.cmr.model.FileInfo, Boolean> userHome = getOrCreateNode(parentNodeRef, name, ContentModel.TYPE_FOLDER);
        if (userHome.getSecond())
        {
            NodeRef nodeRef = userHome.getFirst().getNodeRef();
            permissionService.setPermission(nodeRef, name, permissionService.getAllPermission(), true);
            permissionService.setPermission(nodeRef, permissionService.getAllAuthorities(), PermissionService.CONSUMER, true);
            permissionService.setPermission(nodeRef, permissionService.getOwnerAuthority(), permissionService.getAllPermission(), true);
            ownableService.setOwner(nodeRef, name);
            permissionService.setInheritParentPermissions(nodeRef, false);

            properties.put(ContentModel.PROP_HOMEFOLDER, nodeRef);
            if (!personService.personExists(name))
            {
                personService.createPerson(properties);
            }
            if (!authenticationService.authenticationExists(name))
            {
                authenticationService.createAuthentication(name, password.toCharArray());
            }
        }
    }

    /**
     * Excel 2003 With Versionable file
     *
     * CreateFile 5EE27100 RenameFile oldPath:\Espaces Utilisateurs\System\Cherries.xls, newPath:\Espaces Utilisateurs\System\Cherries.xls~RF172f241.TMP RenameFile oldName=\Espaces Utilisateurs\System\5EE27100, newName=\Espaces Utilisateurs\System\Cherries.xls, session:WNB0
     *
     * Set Delete On Close for Cherries.xls~RF172f241.TMP
     */
    public void testExcel2003SaveShuffle() throws Exception
    {
        // fail("not yet implemented");
        logger.debug("testScenarioExcel2003SaveShuffle");
        final String FILE_NAME = "Cherries.xls";
        final String FILE_TITLE = "Cherries";
        final String FILE_DESCRIPTION = "This is a test document to test CIFS shuffle";
        final String FILE_OLD_TEMP = "Cherries.xls~RF172f241.TMP";
        final String FILE_NEW_TEMP = "5EE27100";

        final QName RESIDUAL_MTTEXT = QName.createQName("{gsxhjsx}", "whatever");

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;
            NetworkFile oldFileHandle;

            NodeRef testNodeRef; // node ref of test.doc

            Serializable testCreatedDate;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testScenarioMSExcel2003SaveShuffle";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        /**
         * Create a file in the test directory
         */

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // now load up the node with lots of other stuff that we will test to see if it gets preserved during the
                // shuffle.
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                // test non CM namespace property
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
                // test CM property not related to an aspect
                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_ADDRESSEE, "Fred");

                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_TITLE, FILE_TITLE);
                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_DESCRIPTION, FILE_DESCRIPTION);

                /**
                 * MLText value - also a residual value in a non cm namespace
                 */
                MLText mltext = new MLText();
                mltext.addValue(Locale.FRENCH, "Bonjour");
                mltext.addValue(Locale.ENGLISH, "Hello");
                mltext.addValue(Locale.ITALY, "Buongiorno");
                mlAwareNodeService.setProperty(testContext.testNodeRef, RESIDUAL_MTTEXT, mltext);

                // classifiable chosen since its not related to any properties.
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_CLASSIFIABLE, null);
                // nodeService.createAssociation(testContext.testNodeRef, targetRef, assocTypeQName);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * Write some content to the test file
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                String testContent = "MS Excel 2003 shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.firstFileHandle.close();

                testContext.testCreatedDate = nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_CREATED);

                MLText multi = (MLText) mlAwareNodeService.getProperty(testContext.testNodeRef, RESIDUAL_MTTEXT);
                multi.getValues();

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * b) Save the new file
         */
        RetryingTransactionCallback<Void> saveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);
                String testContent = "MS Word 2003 shuffle test This is new content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.newFileHandle.close();

                return null;
            }
        };
        tran.doInTransaction(saveNewFileCB, false, true);

        /**
         * rename the old file
         */
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        RetryingTransactionCallback<Void> validateOldFileGoneCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                try
                {
                    driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                }
                catch (IOException e)
                {
                    // expect to go here since previous step renamed the file.
                }

                return null;
            }
        };
        tran.doInTransaction(validateOldFileGoneCB, false, true);

        /**
         * Move the new file into place, stuff should get shuffled
         */
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                // Check trx:enabled has been shuffled.
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
                // check my residual MLText has been transferred
                assertTrue(props.containsKey(RESIDUAL_MTTEXT));

                // Check the titled aspect is correct
                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));
                assertEquals("title wrong", FILE_TITLE, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_TITLE));
                assertEquals("description wrong", FILE_DESCRIPTION, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_DESCRIPTION));

                // commented out due to ALF-7641
                // CIFS shuffle, does not preseve MLText values.
                // Map<QName, Serializable> mlProps = mlAwareNodeService.getProperties(shuffledNodeRef);

                // MLText multi = (MLText)mlAwareNodeService.getProperty(shuffledNodeRef, RESIDUAL_MTTEXT) ;
                // multi.getValues();

                // check auditable properties
                // commented out due to ALF-7635
                // assertEquals("creation date not preserved", ((Date)testContext.testCreatedDate).getTime(), ((Date)nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_CREATED)).getTime());

                // commented out due to ALF-7628
                // assertEquals("ADDRESSEE PROPERTY Not copied", "Fred", nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_ADDRESSEE));
                // assertTrue("CLASSIFIABLE aspect not present", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_CLASSIFIABLE));

                // commented out due to ALF-7584.
                // assertEquals("noderef changed", testContext.testNodeRef, shuffledNodeRef);
                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };

        tran.doInTransaction(deleteOldFileCB, false, true);

    }

    /**
     * Excel 2013 With Versionable file (MNT-13078)
     *
     * CreateFile Cherries.xlsx CreateFile ~herries.tmp CreateFile Cherries.xlsx~RF172f241.TMP DeleteFile Cherries.xlsx~RF172f241.TMP RenameFile oldName: Cherries.xls, newName: Cherries.xlsx~RF172f241.TMP Delete On Close for Cherries.xlsx~RF172f241.TMP RenameFile oldName: ~herries.tmp, newName: Cherries.xlsx
     *
     */
    public void testExcel2013SaveShuffle() throws Exception
    {
        logger.debug("testScenarioExcel2013SaveShuffle");
        final String FILE_NAME = "Cherries.xlsx";
        final String FILE_ORIGINAL_TITLE = "Original";
        final String FILE_UNUSED_TEMP = "Cherries.xlsx~RF172f241.TMP";
        final String FILE_USED_TEMP = "~herries.tmp";
        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testScenarioMSExcel2013SaveShuffle";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NodeRef testNodeRef; // node ref of test.doc
        }
        ;

        final TestContext testContext = new TestContext();

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        /**
         * Delete a file in the test directory
         */

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createOriginalFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);
                String testContent = "MS Word 2013 shuffle test. This is first file content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.firstFileHandle.close();

                // now load up the node with lots of other stuff that we will test to see if it gets preserved during the
                // shuffle.
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);
                // need to remove. Otherwise file will be deleted (not placed into archive spaces store)
                nodeService.removeAspect(testContext.testNodeRef, ContentModel.ASPECT_NO_CONTENT);
                // test non CM namespace property
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);

                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_TITLE, FILE_ORIGINAL_TITLE);

                return null;
            }
        };
        tran.doInTransaction(createOriginalFileCB, false, true);

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createUsedTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_USED_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                NetworkFile fileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(fileHandle);
                String testContent = "MS Word 2013 shuffle test. This is used file content";
                byte[] testContentBytes = testContent.getBytes();
                fileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                fileHandle.close();

                NodeRef usedRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_USED_TEMP);

                nodeService.addAspect(usedRef, ContentModel.ASPECT_VERSIONABLE, null);
                nodeService.removeAspect(usedRef, ContentModel.ASPECT_NO_CONTENT);
                nodeService.setProperty(usedRef, ContentModel.PROP_TITLE, "Used");

                return null;
            }
        };
        tran.doInTransaction(createUsedTempFileCB, false, true);

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createUnusedTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_UNUSED_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                NetworkFile unusedFile = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(unusedFile);

                NodeRef unusedNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_UNUSED_TEMP);
                nodeService.addAspect(unusedNodeRef, ContentModel.ASPECT_VERSIONABLE, null);
                nodeService.setProperty(unusedNodeRef, TransferModel.PROP_ENABLED, true);
                nodeService.setProperty(unusedNodeRef, ContentModel.PROP_TITLE, "Unused");

                return null;
            }
        };
        tran.doInTransaction(createUnusedTempFileCB, false, true);

        /**
         * Delete unused temporary file
         */
        RetryingTransactionCallback<Void> deleteUnusedFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                try
                {
                    driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_UNUSED_TEMP);
                }
                catch (IOException e)
                {
                    // expect to go here since previous step renamed the file.
                }

                return null;
            }
        };
        tran.doInTransaction(deleteUnusedFileCB, false, true);

        /**
         * Rename the original file to unused file
         */
        RetryingTransactionCallback<Void> renameToUnusedFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_UNUSED_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameToUnusedFileCB, false, true);

        /**
         * Delete unused temporary file
         */
        tran.doInTransaction(deleteUnusedFileCB, false, true);

        /**
         * Rename the used temporary file to original file
         */
        RetryingTransactionCallback<Void> renameToUsedFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_USED_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(renameToUsedFileCB, false, true);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                // Check trx:enabled has been shuffled.
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
                assertTrue("node doesn't contain property 'TITLE'", props.containsKey(ContentModel.PROP_TITLE));
                assertEquals("propety 'TITLE' isn't correct", FILE_ORIGINAL_TITLE, props.get(ContentModel.PROP_TITLE));
                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));

                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);
    }

    /**
     * Excel 2003 CSV file with Versionable file
     * 
     * CreateFile csv.csv and 5EE27101 Add versionable aspect RenameFile oldPath:\Espaces Utilisateurs\System\csv.csv, newPath:\Espaces\Utilisateurs\System\5EE27101 CreateFile name=\Espaces Utilisateurs\System\csv.csv Add content
     */
    public void testCSVExcel2003SaveShuffle() throws Exception
    {
        logger.debug("testCSVExcel2003SaveShuffle");
        final String FILE_NAME = "csv.csv";
        final String FILE_TITLE = "csv";
        final String FILE_DESCRIPTION = "This is a test document to test CIFS shuffle";
        final String FILE_TEMP = "AAAA0000";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;

            NodeRef testNodeRef;

            Serializable testCreatedDate;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testMSExcel2003CSVShuffle";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createTestFileFirstTime = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_TITLE, FILE_TITLE);
                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_DESCRIPTION, FILE_DESCRIPTION);

                return null;
            }
        };
        tran.doInTransaction(createTestFileFirstTime, false, true);

        /**
         * Write some content to the test file. Add versionable aspect
         */
        RetryingTransactionCallback<Void> writeToTestFileAndAddVersionableAspect = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                String testContent = "MS Excel 2003 for CSV shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.firstFileHandle.close();

                testContext.testCreatedDate = nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_CREATED);

                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, Collections
                        .<QName, Serializable> singletonMap(ContentModel.PROP_VERSION_TYPE,
                                org.alfresco.service.cmr.version.VersionType.MINOR));

                return null;
            }
        };
        tran.doInTransaction(writeToTestFileAndAddVersionableAspect, false, true);

        /**
         * rename the test file to the temp
         */
        RetryingTransactionCallback<Void> renameTestFileToTemp = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameTestFileToTemp, false, true);

        /**
         * create the test file one more
         */
        RetryingTransactionCallback<Void> createTestFileOneMore = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                FileOpenParams params = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, FileAction.TruncateExisting, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                NetworkFile file = driver.createFile(testSession, testConnection, params);
                driver.closeFile(testSession, testConnection, file);

                return null;
            }
        };
        tran.doInTransaction(createTestFileOneMore, false, true);

        /**
         * Write the new content
         */
        RetryingTransactionCallback<Void> writeUpdate = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.openFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);
                String testContent = "MS Word 2003 for CSV shuffle test This is new content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.newFileHandle.close();

                return null;
            }
        };
        tran.doInTransaction(writeUpdate, false, true);

        // Check results
        RetryingTransactionCallback<Void> validate = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                // Check versionable aspect, version label and nodeRef
                assertTrue("VERSIONABLE aspect not present", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertEquals("nodeRef changed", testContext.testNodeRef, shuffledNodeRef);

                // Check the titled aspect is correct
                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));
                assertEquals("title wrong", FILE_TITLE, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_TITLE));
                assertEquals("description wrong", FILE_DESCRIPTION, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_DESCRIPTION));

                return null;
            }
        };

        tran.doInTransaction(validate, true, true);

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteTestFile = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(deleteTestFile, false, true);

    }

    /**
     * Simulates a SaveAs from Word2003 1. Create new document SAVEAS.DOC, file did not exist 2. Create -WRDnnnn.TMP file, where 'nnnn' is a 4 digit sequence to make the name unique 3. Rename SAVEAS.DOC to Backup of SAVEAS.wbk 4. Rename -WRDnnnn.TMP to SAVEAS.DOC
     */
    public void testScenarioMSWord2003SaveAsShuffle() throws Exception
    {
        logger.debug("testScenarioMSWord2003SaveShuffle");
        final String FILE_NAME = "SAVEAS.DOC";
        final String FILE_OLD_TEMP = "SAVEAS.wbk";
        final String FILE_NEW_TEMP = "~WRD0002.TMP";

        class TestContext
        {
            NetworkFile firstFileHandle;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testScenarioMSWord2003SaveAsShuffle";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        /**
         * Create a file in the test directory
         */

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("a) create new file");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * b) Save the new file Write ContentDiskDriverTest3.doc to the test file,
         */
        logger.debug("b) move new file into place");
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource);

                byte[] buffer = new byte[1000];
                InputStream is = fileResource.getInputStream();
                try
                {
                    long offset = 0;
                    int i = is.read(buffer, 0, buffer.length);
                    while (i > 0)
                    {
                        testContext.firstFileHandle.writeFile(buffer, i, 0, offset);
                        offset += i;
                        i = is.read(buffer, 0, buffer.length);
                    }
                }
                finally
                {
                    is.close();
                }

                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * c) rename the old file
         */
        logger.debug("c) rename old file");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * d) Move the new file into place, stuff should get shuffled
         */
        logger.debug("d) move new file into place");
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        logger.debug("e) validate results");
        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("size is wrong", 26112, data.getSize());
                assertEquals("mimeType is wrong", "application/msword", data.getMimetype());

                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);

    }

    /**
     * Test Open Close File Scenario
     * 
     * 1) open(readOnly) 2) open(readWrite) 3) open(readWrite) - does nothing. 4) close - does nothing 5) close - does nothing 6) close - updates the repo
     */
    public void testScenarioOpenCloseFile() throws Exception
    {
        logger.debug("start of testScenarioOpenCloseFile");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        class TestContext
        {
            NodeRef testDirNodeRef;
            NodeRef targetNodeRef;
        }
        ;

        final TestContext testContext = new TestContext();

        final String FILE_NAME = "testScenarioOpenFile.txt";
        final String FILE_PATH = TEST_ROOT_DOS_PATH + "\\" + FILE_NAME;

        FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);
        driver.createDirectory(testSession, testConnection, dirParams);

        testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_ROOT_DOS_PATH);

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, FILE_PATH);
                return null;
            }
        };
        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        /**
         * Step 1: Now create the file through the node service and open it.
         */
        logger.debug("Step 1) Create File and Open file created by node service");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                logger.debug("create file and close it immediatly");
                FileOpenParams createFileParams = new FileOpenParams(FILE_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                NetworkFile dummy = driver.createFile(testSession, testConnection, createFileParams);
                assertFalse("file is closed after create", dummy.isClosed());
                driver.closeFile(testSession, testConnection, dummy);
                logger.debug("after create and close");
                // TODO Bug in JavaNetworkFile
                // assertTrue("file is not closed after close", dummy.isClosed());
                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        testContext.targetNodeRef = getNodeForPath(testConnection, FILE_PATH);

        FileOpenParams openRO = new FileOpenParams(FILE_PATH, FileAction.CreateNotExist, AccessMode.ReadOnly, FileAttribute.NTNormal, 0);
        FileOpenParams openRW = new FileOpenParams(FILE_PATH, FileAction.CreateNotExist, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);

        /**
         * First open - read only
         */
        logger.debug("open file1 read only");
        NetworkFile file1 = driver.openFile(testSession, testConnection, openRO);
        assertNotNull(file1);
        assertFalse("file1 is closed", file1.isClosed());

        final String testString = "Yankee doodle went to town";
        byte[] stuff = testString.getBytes("UTF-8");

        /**
         * Negative test - file is open readOnly
         */
        try
        {
            driver.writeFile(testSession, testConnection, file1, stuff, 0, stuff.length, 0);
            fail("can write to a read only file!");
        }
        catch (Exception e)
        {
            // Expect to go here
        }

        logger.debug("open file 2 for read write");
        NetworkFile file2 = driver.openFile(testSession, testConnection, openRW);
        assertNotNull(file2);
        assertFalse("file is closed", file2.isClosed());

        /**
         * Write Some Content
         */
        driver.writeFile(testSession, testConnection, file2, stuff, 0, stuff.length, 0);

        NetworkFile file3 = driver.openFile(testSession, testConnection, openRW);
        assertNotNull(file3);

        logger.debug("first close");
        driver.closeFile(testSession, testConnection, file2);
        // assertTrue("node does not have no content aspect", nodeService.hasAspect(testContext.targetNodeRef, ContentModel.ASPECT_NO_CONTENT));

        logger.debug("second close");
        driver.closeFile(testSession, testConnection, file3);
        // //assertTrue("node does not have no content aspect", nodeService.hasAspect(testContext.targetNodeRef, ContentModel.ASPECT_NO_CONTENT));

        // logger.debug("this should be the last close");
        // driver.closeFile(testSession, testConnection, file1);
        // assertFalse("node still has no content aspect", nodeService.hasAspect(testContext.targetNodeRef, ContentModel.ASPECT_NO_CONTENT));

        /**
         * Step 2: Negative test - Close the file again - should do nothing quietly!
         */
        logger.debug("this is a negative test - should do nothing");
        driver.closeFile(testSession, testConnection, file1);

        logger.debug("now validate");

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                Map<QName, Serializable> props = nodeService.getProperties(testContext.targetNodeRef);
                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("data wrong length", testString.length(), data.getSize());

                ContentReader reader = contentService.getReader(testContext.targetNodeRef, ContentModel.PROP_CONTENT);
                String s = reader.getContentString();
                assertEquals("content not written", testString, s);

                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

    } // testOpenCloseFileScenario

    /**
     * Test Open Close File Scenario II ALF-13401 Open Read Only of a file already open for read/write.
     * 
     * 1) open(readWrite) 2) write some content. 3) open(readOnly). 4) read some content. 5) close - updates the repo
     */
    public void testScenarioOpenCloseFileTwo() throws Exception
    {
        logger.debug("start of testScenarioOpenCloseFileTwo");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        class TestContext
        {
            NodeRef testDirNodeRef;
            NodeRef targetNodeRef;
        }
        ;

        final TestContext testContext = new TestContext();

        final String FILE_NAME = "testScenarioOpenFileTwo.txt";
        final String FILE_PATH = TEST_ROOT_DOS_PATH + "\\" + FILE_NAME;

        FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);
        driver.createDirectory(testSession, testConnection, dirParams);

        testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_ROOT_DOS_PATH);

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, FILE_PATH);
                return null;
            }
        };
        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        /**
         * Step 1: Now create the file through the node service and open it.
         */
        logger.debug("Step 1) Create File and Open file created by node service");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                logger.debug("create file and close it immediatly");
                FileOpenParams createFileParams = new FileOpenParams(FILE_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                NetworkFile dummy = driver.createFile(testSession, testConnection, createFileParams);
                driver.closeFile(testSession, testConnection, dummy);
                logger.debug("after create and close");
                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        testContext.targetNodeRef = getNodeForPath(testConnection, FILE_PATH);

        FileOpenParams openRO = new FileOpenParams(FILE_PATH, FileAction.CreateNotExist, AccessMode.ReadOnly, FileAttribute.NTNormal, 0);
        FileOpenParams openRW = new FileOpenParams(FILE_PATH, FileAction.CreateNotExist, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);

        /**
         * First open - read write
         */
        logger.debug("open file1 read only");
        NetworkFile file1 = driver.openFile(testSession, testConnection, openRW);
        assertNotNull(file1);

        final String testString = "Yankee doodle went to town, riding on a donkey.";
        byte[] stuff = testString.getBytes("UTF-8");

        driver.writeFile(testSession, testConnection, file1, stuff, 0, stuff.length, 0);

        logger.debug("open file 2 for read only");
        NetworkFile file2 = driver.openFile(testSession, testConnection, openRO);
        assertNotNull(file2);

        assertTrue("file size is 0", file2.getFileSize() > 0);

        /**
         * Write Some More Content
         */
        driver.writeFile(testSession, testConnection, file1, stuff, 0, stuff.length, 0);

        logger.debug("first close");
        driver.closeFile(testSession, testConnection, file2);

        logger.debug("second close");
        driver.closeFile(testSession, testConnection, file1);

        logger.debug("now validate");

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                Map<QName, Serializable> props = nodeService.getProperties(testContext.targetNodeRef);
                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("data wrong length", testString.length(), data.getSize());

                ContentReader reader = contentService.getReader(testContext.targetNodeRef, ContentModel.PROP_CONTENT);
                String s = reader.getContentString();
                assertEquals("content not written", testString, s);

                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

    } // testOpenCloseFileScenarioTwo

    /**
     * Unit test of open read/write close versionable file - should not do anything.
     * <p>
     * This is done with a CIFS shuffle from word. Basically Word holds the file open with a read/write lock while the shuffle is going on.
     * <p>
     * Create a file. Apply versionable aspect Open the file ReadWrite + OpLocks Close the file Check Version has not incremented.
     */
    public void testOpenCloseVersionableFile() throws Exception
    {
        logger.debug("testOpenCloseVersionableFile");

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);

        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        final String FILE_PATH1 = TEST_ROOT_DOS_PATH + "\\OpenCloseFile.new";

        class TestContext
        {}
        ;

        final TestContext testContext = new TestContext();

        FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);
        driver.createDirectory(testSession, testConnection, dirParams);

        FileOpenParams params1 = new FileOpenParams(FILE_PATH1, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
        NetworkFile file1 = driver.createFile(testSession, testConnection, params1);
        driver.closeFile(testSession, testConnection, file1);

        /**
         * Make Node 1 versionable
         */
        RetryingTransactionCallback<Void> makeVersionableCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef file1NodeRef = getNodeForPath(testConnection, FILE_PATH1);
                nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                ContentWriter contentWriter2 = contentService.getWriter(file1NodeRef, ContentModel.PROP_CONTENT, true);
                contentWriter2.putContent("test open close versionable node");

                return null;
            }
        };
        tran.doInTransaction(makeVersionableCB, false, true);

        RetryingTransactionCallback<String> readVersionCB = new RetryingTransactionCallback<String>() {

            @Override
            public String execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, FILE_PATH1);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                assertTrue("versionable aspect not present", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                props.get(ContentModel.PROP_VERSION_LABEL);

                return (String) props.get(ContentModel.PROP_VERSION_LABEL);
            }
        };

        String version = tran.doInTransaction(readVersionCB, false, true);

        /**
         * Step 1: Open The file Read/Write TODO Check primary assoc, peer assocs, child assocs, modified date, created date, nodeid, permissions.
         */
        NetworkFile file = driver.openFile(testSession, testConnection, params1);

        assertNotNull("file is null", file);

        /**
         * Step 2: Close the file
         */
        driver.closeFile(testSession, testConnection, file);

        /**
         * Validate that there is no version increment.
         */
        String version2 = tran.doInTransaction(readVersionCB, false, true);

        assertEquals("version has incremented", version, version2);

        /**
         * Now do an update and check the version increments
         */
        file = driver.openFile(testSession, testConnection, params1);

        assertNotNull("file is null", file);

        byte[] stuff = "Hello World".getBytes();
        driver.writeFile(testSession, testConnection, file, stuff, 0, stuff.length, 0);

        /**
         * Step 2: Close the file
         */
        driver.closeFile(testSession, testConnection, file);

        String version3 = tran.doInTransaction(readVersionCB, false, true);

        assertFalse("version not incremented", version.equals(version3));

    } // OpenCloseVersionableFile

    /**
     * Frame maker save a) Lock File Created (X.fm.lck) b) Create new file (X.fm.C29) c) Existing file rename out of the way. (X.backup.fm) d) New file rename into place. (X.fm.C29) e) Old file deleted (open with delete on close) f) Lock file deleted (open with delete on close)
     */
    public void testScenarioFrameMakerShuffle() throws Exception
    {
        logger.debug("testScenarioFramemakerShuffle");

        final String LOCK_FILE = "X.fm.lck";
        final String FILE_NAME = "X.fm";
        final String FILE_OLD_TEMP = "X.backup.fm";
        final String FILE_NEW_TEMP = "X.fm.C29";

        class TestContext
        {
            NetworkFile firstFileHandle;
            String mimetype;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testScenarioFramemakerShuffle";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("a) create new file");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);
                ClassPathResource fileResource = new ClassPathResource("filesys/X1.fm");
                assertNotNull("unable to find test resource filesys/X1.fm", fileResource);
                writeResourceToNetworkFile(fileResource, testContext.firstFileHandle);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * b) Save the new file Write X2.fm to the test file,
         */
        logger.debug("b) move new file into place");
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);

                ClassPathResource fileResource = new ClassPathResource("filesys/X2.fm");
                assertNotNull("unable to find test resource filesys/X2.fm", fileResource);
                writeResourceToNetworkFile(fileResource, testContext.firstFileHandle);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                Map<QName, Serializable> props = nodeService.getProperties(file1NodeRef);
                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("size is wrong", 166912, data.getSize());
                testContext.mimetype = data.getMimetype();

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * c) rename the old file
         */
        logger.debug("c) rename old file");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * d) Move the new file into place, stuff should get shuffled
         */
        logger.debug("d) move new file into place");
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        /**
         * d) Delete the old file
         */
        logger.debug("d) move new file into place");
        RetryingTransactionCallback<Void> deleteOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };

        tran.doInTransaction(deleteOldFileCB, false, true);

        logger.debug("e) validate results");

        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("size is wrong", 123904, data.getSize());

                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertTrue("file has lost versionable aspect", nodeService.hasAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE));

                assertEquals("mimeType is wrong", testContext.mimetype, data.getMimetype());

                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);
    } // Scenario frame maker save

    /**
     * Test that rules fire on zero byte long files. In this case check that a new file gets the versionable aspect added.
     */
    public void testZeroByteRules() throws Exception
    {
        logger.debug("testZeroByteRules");
        final String FILE_NAME_ZERO = "Zero.docx";
        final String FILE_NAME_NON_ZERO = "NonZero.docx";

        class TestContext
        {
            NodeRef testDirNodeRef;
            NodeRef testZeroNodeRef;
            NodeRef testNonZeroNodeRef;
            NetworkFile firstFileHandle;
            NetworkFile secondFileHandle;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testZeroByteRules";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageDirCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteDirectory(testSession, testConnection, TEST_DIR);
                return null;
            }
        };

        try
        {
            tran.doInTransaction(deleteGarbageDirCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("create Test directory" + TEST_DIR);
        RetryingTransactionCallback<Void> createTestDirCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_DIR);
                assertNotNull("testDirNodeRef is null", testContext.testDirNodeRef);
                return null;

            }
        };
        tran.doInTransaction(createTestDirCB);
        logger.debug("Create rule on test dir");

        RetryingTransactionCallback<Void> createRuleCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                Rule rule = new Rule();
                rule.setRuleType(RuleType.INBOUND);
                rule.applyToChildren(true);
                rule.setRuleDisabled(false);
                rule.setTitle("Make Versionable");
                rule.setDescription("ContentDiskDriverTest Test Zero Byte files");

                Map<String, Serializable> props = new HashMap<String, Serializable>(1);
                props.put("aspect-name", ContentModel.ASPECT_VERSIONABLE);
                Action addVersionable = actionService.createAction("add-features", props);

                ActionCondition noCondition1 = actionService.createActionCondition(NoConditionEvaluator.NAME);
                addVersionable.addActionCondition(noCondition1);

                ActionCondition noCondition2 = actionService.createActionCondition(NoConditionEvaluator.NAME);
                CompositeAction compAction = actionService.createCompositeAction();
                compAction.setTitle("Make Versionablea");
                compAction.setDescription("Add Aspect - Versionable");
                compAction.addAction(addVersionable);
                compAction.addActionCondition(noCondition2);

                rule.setAction(compAction);

                ruleService.saveRule(testContext.testDirNodeRef, rule);

                logger.debug("add aspect versionable rule created");

                return null;
            }
        };
        tran.doInTransaction(createRuleCB, false, true);

        /**
         * Create a file in the test directory
         */
        logger.debug("create test file in test directory");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the zero byte file we are going to use to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME_ZERO, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                testContext.testZeroNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME_ZERO);
                assertNotNull("testContext.testNodeRef is null", testContext.testZeroNodeRef);

                /**
                 * Create the non zero byte file we are going to use to test
                 */
                FileOpenParams createFileParams2 = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME_NON_ZERO, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.secondFileHandle = driver.createFile(testSession, testConnection, createFileParams2);
                assertNotNull(testContext.secondFileHandle);

                testContext.testNonZeroNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME_NON_ZERO);
                assertNotNull("testContext.testNodeRef is null", testContext.testNonZeroNodeRef);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        logger.debug("step b: close the file with zero byte content");

        /**
         * Write ContentDiskDriverTest1.docx to the test file,
         */
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                logger.debug("close the file, firstFileHandle");
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                // Write hello world into the second file
                byte[] stuff = "Hello World".getBytes();
                driver.writeFile(testSession, testConnection, testContext.secondFileHandle, stuff, 0, stuff.length, 0);

                logger.debug("close the second non zero file, secondFileHandle");
                driver.closeFile(testSession, testConnection, testContext.secondFileHandle);

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        logger.debug("Step c: validate versioble aspect has been applied.");

        /**
         * c: check zero byte file has the versionable aspect.
         */
        RetryingTransactionCallback<Void> validateFirstExtractionCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                assertTrue("versionable aspect not applied to non zero file.", nodeService.hasAspect(testContext.testNonZeroNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertTrue("versionable aspect not applied to zero byte file.", nodeService.hasAspect(testContext.testZeroNodeRef, ContentModel.ASPECT_VERSIONABLE));
                return null;
            }
        };
        tran.doInTransaction(validateFirstExtractionCB, false, true);
    } // testZeroByteRules

    /**
     * Test that files can be created with empty content and that existing content can be over-wrriten by empty content.
     */
    public void testEmptyContent() throws Exception
    {
        logger.debug("testEmptyContent");
        final String FILE_NAME_ZERO = "Zero.docx";
        final String FILE_NAME_NON_ZERO = "NonZero.docx";

        class TestContext
        {
            NodeRef testDirNodeRef;
            NodeRef testZeroNodeRef;
            NodeRef testNonZeroNodeRef;
            NetworkFile firstFileHandle;
            NetworkFile secondFileHandle;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testEmptyContent";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageDirCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteDirectory(testSession, testConnection, TEST_DIR);
                return null;
            }
        };

        try
        {
            tran.doInTransaction(deleteGarbageDirCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("create Test directory" + TEST_DIR);
        RetryingTransactionCallback<Void> createTestDirCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_DIR);
                assertNotNull("testDirNodeRef is null", testContext.testDirNodeRef);
                return null;

            }
        };
        tran.doInTransaction(createTestDirCB);

        /**
         * Create a file in the test directory
         */
        logger.debug("create test file in test directory");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the zero byte file we are going to use to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME_ZERO, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                testContext.testZeroNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME_ZERO);
                assertNotNull("testContext.testNodeRef is null", testContext.testZeroNodeRef);

                logger.debug("close the file, firstFileHandle");
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                /**
                 * Create the non zero byte file we are going to use to test
                 */
                FileOpenParams createFileParams2 = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME_NON_ZERO, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.secondFileHandle = driver.createFile(testSession, testConnection, createFileParams2);
                assertNotNull(testContext.secondFileHandle);

                testContext.testNonZeroNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME_NON_ZERO);
                assertNotNull("testContext.testNodeRef is null", testContext.testNonZeroNodeRef);

                // Write hello world into the second file
                byte[] stuff = "Hello World".getBytes();
                driver.writeFile(testSession, testConnection, testContext.secondFileHandle, stuff, 0, stuff.length, 0);

                logger.debug("close the second non zero file, secondFileHandle");
                driver.closeFile(testSession, testConnection, testContext.secondFileHandle);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * d: check both files have content properties.
         */
        RetryingTransactionCallback<Void> checkContentPropsCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                assertNotNull("content missing create non zero file.", nodeService.getProperty(testContext.testNonZeroNodeRef, ContentModel.PROP_CONTENT));
                assertNotNull("content missing create zero byte file.", nodeService.getProperty(testContext.testZeroNodeRef, ContentModel.PROP_CONTENT));
                return null;
            }
        };
        tran.doInTransaction(checkContentPropsCB, false, true);

        RetryingTransactionCallback<Void> truncateFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Truncate the non zero byte file we are going to use to test
                 */
                FileOpenParams createFileParams2 = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME_NON_ZERO, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.secondFileHandle = driver.openFile(testSession, testConnection, createFileParams2);
                assertNotNull(testContext.secondFileHandle);

                testContext.testNonZeroNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME_NON_ZERO);
                assertNotNull("testContext.testNodeRef is null", testContext.testNonZeroNodeRef);

                driver.truncateFile(testSession, testConnection, testContext.secondFileHandle, 0);

                logger.debug("close the second non zero file, secondFileHandle");
                driver.closeFile(testSession, testConnection, testContext.secondFileHandle);

                return null;
            }
        };
        tran.doInTransaction(truncateFileCB, false, true);

        /**
         * d: check both files have content properties.
         */
        RetryingTransactionCallback<Void> checkContentProps2CB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                ContentReader reader = contentService.getReader(testContext.testNonZeroNodeRef, ContentModel.PROP_CONTENT);
                String s = reader.getContentString();
                assertEquals("content not truncated", "", s);

                ContentReader reader2 = contentService.getReader(testContext.testZeroNodeRef, ContentModel.PROP_CONTENT);
                String s2 = reader2.getContentString();
                assertEquals("content not empty", "", s2);
                return null;
            }
        };
        tran.doInTransaction(checkContentProps2CB, false, true);

    } // testEmptyFiles

    /**
     * Simulates a SaveAs from Word2003 for a checked out file
     * 
     * 1. Create new document TESTFILE.DOC, file did not exist 2. CheckOut TESTFILE.DOC 3. Create -WRDnnnn.TMP file, where 'nnnn' is a 4 digit sequence to make the name unique 4. Rename TESTFILE(Working Copy).DOC to Backup of SAVEAS.wbk 5. Rename -WRDnnnn.TMP to TESTFILE(Working Copy).DOC 6 CheckIn working copy. 7. Validate TESTFILE.DOC
     */
    public void testScenarioMSWord2003SaveAsShuffleCheckedOutFile() throws Exception
    {
        logger.debug("testScenarioMSWord2003SaveShuffleLockedFile");
        final String FILE_NAME = "TESTFILE.DOC";
        final String FILE_OLD_TEMP = "SAVEAS.wbk";
        final String FILE_NEW_TEMP = "~WRD0002.TMP";

        class TestContext
        {
            NetworkFile firstFileHandle;
            String workingFileName;
            NodeRef workingCopy;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testScenarioMSWord2003ShuffleLockedFile";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        /**
         * Create a file in the test directory
         */

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("a) create new file and check out");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                /**
                 * CheckOut the test node
                 */
                NodeRef workingCopy = checkOutCheckInService.checkout(shuffledNodeRef);
                assertNotNull("Working copy is null", workingCopy);
                testContext.workingCopy = workingCopy;

                ChildAssociationRef ref = nodeService.getPrimaryParent(workingCopy);
                QName name = ref.getQName();
                testContext.workingFileName = ref.getQName().getLocalName();
                assertNotNull("working file name is null", testContext.workingFileName);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * b) Save the new file Write ContentDiskDriverTest3.doc to the test file,
         */
        logger.debug("b) move new file into place");
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource);

                byte[] buffer = new byte[1000];
                InputStream is = fileResource.getInputStream();
                try
                {
                    long offset = 0;
                    int i = is.read(buffer, 0, buffer.length);
                    while (i > 0)
                    {
                        testContext.firstFileHandle.writeFile(buffer, i, 0, offset);
                        offset += i;
                        i = is.read(buffer, 0, buffer.length);
                    }
                }
                finally
                {
                    is.close();
                }

                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * c) rename the old working file
         */
        logger.debug("c) rename old file");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + testContext.workingFileName, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * d) Move the new file into place, stuff should get shuffled
         */
        logger.debug("d) move new file into place");
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + testContext.workingFileName);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        logger.debug("e) now check in");

        /**
         * Now Check In
         */
        RetryingTransactionCallback<Void> checkInCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                checkOutCheckInService.checkin(testContext.workingCopy, null);
                return null;
            }
        };

        tran.doInTransaction(checkInCB, false, true);

        logger.debug("e) validate results");
        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("size is wrong", 26112, data.getSize());
                assertEquals("mimeType is wrong", "application/msword", data.getMimetype());

                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);

    } // Test Word Save Locked File

    /**
     * ALF-10686 This scenario is executed by windows explorer.
     * 
     * A file is created and the file handle kept open. stuff is written Then the modified date is set Then the file is closed.
     * 
     * @throws Exception
     */
    public void testSetFileScenario() throws Exception
    {
        logger.debug("testSetFileInfo");
        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        Date now = new Date();

        // CREATE 6 hours ago
        final Date CREATED = new Date(now.getTime() - 1000 * 60 * 60 * 6);
        // Modify one hour ago
        final Date MODIFIED = new Date(now.getTime() - 1000 * 60 * 60 * 1);

        class TestContext
        {
            NodeRef testNodeRef;
        }
        ;

        final TestContext testContext = new TestContext();

        /**
         * Step 1 : Create a new file in read/write mode and add some content. Call SetInfo to set the creation date
         */
        int openAction = FileAction.CreateNotExist;

        final String FILE_NAME = "testSetFileScenario.txt";
        final String FILE_PATH = "\\" + FILE_NAME;

        // Clean up junk if it exists
        try
        {
            driver.deleteFile(testSession, testConnection, FILE_PATH);
        }
        catch (IOException ie)
        {
            // expect to go here
        }

        final FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);

        final NetworkFile file = driver.createFile(testSession, testConnection, params);
        assertNotNull("file is null", file);
        assertFalse("file is read only, should be read-write", file.isReadOnly());

        RetryingTransactionCallback<Void> writeStuffCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                byte[] stuff = "Hello World".getBytes();

                driver.writeFile(testSession, testConnection, file, stuff, 0, stuff.length, 0);

                FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
                info.setFileInformationFlags(FileInfo.SetModifyDate);
                info.setModifyDateTime(MODIFIED.getTime());
                info.setNetworkFile(file);
                driver.setFileInformation(testSession, testConnection, FILE_PATH, info);

                return null;
            }
        };
        tran.doInTransaction(writeStuffCB);

        RetryingTransactionCallback<Void> closeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                // This close is in a different position to the simple setFileInformation scenarios above.
                driver.closeFile(testSession, testConnection, file);

                return null;
            }
        };
        tran.doInTransaction(closeFileCB);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef companyHome = repositoryHelper.getCompanyHome();
                NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
                testContext.testNodeRef = newNode;
                assertNotNull("can't find new node", newNode);
                Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT);
                assertNotNull("content is null", content);
                Date modified = (Date) nodeService.getProperty(newNode, ContentModel.PROP_MODIFIED);
                assertEquals("modified time not set correctly", MODIFIED, modified);
                return null;
            }
        };
        tran.doInTransaction(validateCB);

        // clean up so we could run the test again
        RetryingTransactionCallback<Void> deleteFile = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, FILE_PATH);
                return null;
            }
        };
        tran.doInTransaction(deleteFile, false, true);

    } // test set modified scenario

    /**
     * This test tries to simulate the cifs shuffling that is done from Save from Mac Lion by TextEdit
     * 
     * a) Temp file created in temporary folder (test.txt) b) Resource fork file created in temporary folder (._test.txt) b) Target file deleted c) Temp file moved to target file.
     */
    public void testScenarioMacLionTextEdit() throws Exception
    {
        logger.debug("testScenarioLionTextEdit");
        final String FILE_NAME = "test.txt";
        final String FORK_FILE_NAME = "._test.txt";
        final String TEMP_FILE_NAME = "test.txt";

        final String UPDATED_TEXT = "Mac Lion Text Updated Content";

        class TestContext
        {
            NetworkFile lockFileHandle;
            NetworkFile firstFileHandle;
            NetworkFile tempFileHandle;
            NodeRef testNodeRef; // node ref of test.doc
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioLionTextEdit";
        final String TEST_TEMP_DIR = "\\ContentDiskDriverTest\\testScenarioLionTextEdit\\.Temporary Items";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);
                driver.createDirectory(testSession, testConnection, createTempDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                String testContent = "Mac Lion Text";
                byte[] testContentBytes = testContent.getBytes();

                driver.writeFile(testSession, testConnection, testContext.firstFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                /**
                 * Create the temp file we are going to use
                 */
                FileOpenParams createTempFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.tempFileHandle = driver.createFile(testSession, testConnection, createTempFileParams);
                assertNotNull(testContext.tempFileHandle);

                testContent = UPDATED_TEXT;
                testContentBytes = testContent.getBytes();
                driver.writeFile(testSession, testConnection, testContext.tempFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.tempFileHandle);

                /**
                 * Create the temp resource fork file we are going to use
                 */
                createFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + FORK_FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.lockFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.lockFileHandle);
                testContext.lockFileHandle.closeFile();

                /**
                 * Also add versionable to target file
                 */
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * b) Delete the target file
         */
        RetryingTransactionCallback<Void> deleteTargetFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(deleteTargetFileCB, false, true);

        /**
         * c) Move the temp file into place
         */
        RetryingTransactionCallback<Void> moveTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, TEST_DIR + "\\" + FILE_NAME);
                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + FORK_FILE_NAME, TEST_DIR + "\\" + FORK_FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(moveTempFileCB, false, true);

        /**
         * Validate results.
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                assertEquals("shuffledNode ref is different", shuffledNodeRef, testContext.testNodeRef);
                assertTrue("node is not versionable", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));

                ContentReader reader = contentService.getReader(shuffledNodeRef, ContentModel.PROP_CONTENT);
                assertNotNull("Reader is null", reader);
                String s = reader.getContentString();
                assertEquals("content not written", UPDATED_TEXT, s);

                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

    } // testScenarioLionTextEdit

    /**
     * Simulates a Save from Powerpoint 2011 Mac 0. FileA.pptx already exists. 1. Create new document FileA1.pptx 2. Delete FileA.pptx 3. Rename FileA1.pptx to FileA.pptx
     */
    public void testScenarioMSPowerpoint2011MacSaveShuffle() throws Exception
    {
        logger.debug("testScenarioMSPowerpoint2011MacSaveShuffle(");

        final String FILE_NAME = "FileA.pptx";
        final String FILE_NEW_TEMP = "FileA1.pptx";

        class TestContext
        {
            NetworkFile firstFileHandle;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testScenarioMSPowerpoint2011MacSaveShuffle";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "cifs", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        /**
         * Create a file in the test directory
         */

        try
        {
            logger.debug("expect to get exception - cleaning garbage");
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("0) create new file");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use (FileA.pptx)
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * b) Save the new file Write ContentDiskDriverTest3.doc to the test file,
         */
        logger.debug("b) write some content");
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource);

                byte[] buffer = new byte[1000];
                InputStream is = fileResource.getInputStream();
                try
                {
                    long offset = 0;
                    int i = is.read(buffer, 0, buffer.length);
                    while (i > 0)
                    {
                        testContext.firstFileHandle.writeFile(buffer, i, 0, offset);
                        offset += i;
                        i = is.read(buffer, 0, buffer.length);
                    }
                }
                finally
                {
                    is.close();
                }

                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * c) delete the old file
         */
        logger.debug("c) delete old file");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * d) Move the new file into place, stuff should get shuffled
         */
        logger.debug("d) rename new file into place");
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        logger.debug("e) validate results");
        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("size is wrong", 26112, data.getSize());
                assertEquals("mimeType is wrong", "application/msword", data.getMimetype());

                assertTrue("versionable aspect missing", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertTrue("hidden aspect still applied", !nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_HIDDEN));
                assertTrue("temporary aspect still applied", !nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_TEMPORARY));

                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);

    } // testScenarioMSPowerpoint2011MacSaveShuffle

    /**
     * Simulates a Save from Excel 2011 Mac 0. FileA.xlsx already exists. 1. Create new document ._A8A09200 2. Delete FileA.xlsx 3. Rename ._A8A09200 to FileA.xlsx
     */
    public void testScenarioMSExcel2011MacSaveShuffle() throws Exception
    {
        logger.debug("testScenarioMSExcel2011MacSaveShuffle(");

        final String FILE_NAME = "FileA.xlsx";
        final String FILE_NEW_TEMP = "._A8A09200";

        class TestContext
        {
            NetworkFile firstFileHandle;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testScenarioMSExcel2011MacSaveShuffle";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "cifs", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        /**
         * Create a file in the test directory
         */

        try
        {
            logger.debug("expect to get exception - cleaning garbage");
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("0) create new file");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use (FileA.xlsx)
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * b) Save the new file Write ContentDiskDriverTest3.doc to the test file,
         */
        logger.debug("b) write some content");
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource);

                byte[] buffer = new byte[1000];
                InputStream is = fileResource.getInputStream();
                try
                {
                    long offset = 0;
                    int i = is.read(buffer, 0, buffer.length);
                    while (i > 0)
                    {
                        testContext.firstFileHandle.writeFile(buffer, i, 0, offset);
                        offset += i;
                        i = is.read(buffer, 0, buffer.length);
                    }
                }
                finally
                {
                    is.close();
                }

                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * c) delete the old file
         */
        logger.debug("c) delete old file");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * d) Move the new file into place, stuff should get shuffled
         */
        logger.debug("d) rename new file into place");
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        logger.debug("e) validate results");
        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("size is wrong", 26112, data.getSize());
                assertEquals("mimeType is wrong", "application/msword", data.getMimetype());

                assertTrue("versionable aspect missing", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertTrue("hidden aspect still applied", !nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_HIDDEN));
                assertTrue("temporary aspect still applied", !nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_TEMPORARY));

                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);

    } // testScenarioMSExcel2011MacSaveShuffle

    /**
     * This test tries to simulate the cifs shuffling that is done to support MS Word 2011 on Mac with backup turned on.
     * 
     * a) TEST.DOCX b) Create new temp file in temp dir Word Work File D_.tmp c) Delete backup file. c) Rename TEST.DOCX to Backup of TEST.docx d) Move temp file to target dir d) Rename Word Work File D_.tmp to TEST.docx
     */
    public void testScenarioMSWord20011MacSaveWithBackup() throws Exception
    {
        logger.debug("testScenarioMSWord20011MacSaveWithBackup");
        final String FILE_NAME = "TEST.DOCX";
        final String FILE_BACKUP = "Backup of TEST.docx";
        final String FILE_NEW_TEMP = "Word Work File D_.tmp";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;
            NodeRef testNodeRef; // node ref of test.doc
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioMSWord20011MacSaveWithBackup";
        final String TEST_TEMP_DIR = "\\ContentDiskDriverTest\\testScenarioMSWord20011MacSaveWithBackup\\.Temporary Items";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "cifs", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        logger.debug("Step 0 - initialise");

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);
                driver.createDirectory(testSession, testConnection, createTempDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // no need to test lots of different properties, that's already been tested above
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                String testContent = "MS Word 2011 shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.firstFileHandle.close();

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * a) Save the temp file in the temp dir
         */
        logger.debug("Step a - create a temp file in the temp dir");
        RetryingTransactionCallback<Void> saveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);
                String testContent = "MS Word 2011 shuffle test This is new content";
                byte[] testContentBytes = testContent.getBytes();
                testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.newFileHandle.close();

                return null;
            }
        };
        tran.doInTransaction(saveNewFileCB, false, true);

        /* Step b not used in test case */

        /**
         * c) rename the target file to a backup file
         */
        logger.debug("Step c - rename the target file");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_BACKUP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * d) Move the new file into target dir
         */
        logger.debug("Step d - move new file into target dir");
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NEW_TEMP);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        /**
         * e) Rename temp file into place.
         */
        logger.debug("Step e - rename temp file into place");
        RetryingTransactionCallback<Void> renameTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(renameTempFileCB, false, true);

        /**
         * Validate
         */

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));
                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

    } // testScenarioMSMacWord20011SaveWithBackup save

    /**
     * Simulates a Mac Lion Drag and Drop 0. ALF-15158.diff already exists and is versionable 1. Delete ALF-15158.diff 2. Create new document ALF-15158.diff
     */
    public void testMacDragAndDrop() throws Exception
    {
        logger.debug("testMacDragAndDrop()");

        final String FILE_NAME = "ALF-15158.diff";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NodeRef file1NodeRef;
        }
        ;

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\MacDragAndDrop";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "cifs", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        /**
         * Create a file in the test directory
         */

        try
        {
            logger.debug("expect to get exception - cleaning garbage");
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("0) create new file");
        RetryingTransactionCallback<TestContext> createFileCB = new RetryingTransactionCallback<TestContext>() {
            @Override
            public TestContext execute() throws Throwable
            {

                TestContext ctx = new TestContext();
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use (FileA.pptx)
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                ctx.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(ctx.firstFileHandle);

                driver.closeFile(testSession, testConnection, ctx.firstFileHandle);

                ctx.file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(ctx.file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return ctx;
            }
        };
        final TestContext testContext = tran.doInTransaction(createFileCB, false, true);

        /**
         * 1) delete the old file
         */
        logger.debug("1) delete old file");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * 2) CreateNewFile and write some new content
         * 
         */
        logger.debug("2) write some content");
        RetryingTransactionCallback<Void> restoreFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource);

                byte[] buffer = new byte[1000];
                InputStream is = fileResource.getInputStream();
                try
                {
                    long offset = 0;
                    int i = is.read(buffer, 0, buffer.length);
                    while (i > 0)
                    {
                        testContext.firstFileHandle.writeFile(buffer, i, 0, offset);
                        offset += i;
                        i = is.read(buffer, 0, buffer.length);
                    }
                }
                finally
                {
                    is.close();
                }

                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                return null;
            }
        };
        tran.doInTransaction(restoreFileCB, false, true);

        logger.debug("3) validate results");
        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("size is wrong", 26112, data.getSize());
                assertEquals("mimeType is wrong", "application/msword", data.getMimetype());

                assertTrue("versionable aspect missing", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertTrue("hidden aspect still applied", !nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_HIDDEN));
                assertTrue("temporary aspect still applied", !nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_TEMPORARY));

                assertEquals("Node ref has changed", shuffledNodeRef, testContext.file1NodeRef);
                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);
        logger.debug("end testMacDragAndDrop");

    } // testMacDragAndDrop

    /**
     * Mountain Lion 2011 Word a) Create new file (Word Work File D2.tmp) (Actually in real life its renamed from a temp directory. c) Existing file rename out of the way. (Word Work File L_5.tmp) d) New file rename into place. (MacWord1.docx) e) Old file deleted
     */
    public void testScenarioMountainLionWord2011() throws Exception
    {
        logger.debug("testScenarioMountainLionWord2011");

        final String FILE_NAME = "MacWord1.docx";
        final String FILE_OLD_TEMP = "Word Work File L_5.tmp";
        final String FILE_NEW_TEMP = "Word Work File D_2.tmp";

        class TestContext
        {
            NetworkFile firstFileHandle;
            String mimetype;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testScenarioMountainLionWord2011";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("a) create new file");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource);
                writeResourceToNetworkFile(fileResource, testContext.firstFileHandle);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * b) Save the new file Write ContentDiskDriverTest3.doc,
         */
        logger.debug("b) move new file into place");
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource);
                writeResourceToNetworkFile(fileResource, testContext.firstFileHandle);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                Map<QName, Serializable> props = nodeService.getProperties(file1NodeRef);
                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                // assertNotNull("data is null", data);
                // assertEquals("size is wrong", 166912, data.getSize());
                testContext.mimetype = data.getMimetype();

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * c) rename the old file
         */
        logger.debug("c) rename old file");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * d) Move the new file into place, stuff should get shuffled
         */
        logger.debug("d) move new file into place");
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        /**
         * d) Delete the old file
         */
        logger.debug("d) delete the old file");
        RetryingTransactionCallback<Void> deleteOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };

        tran.doInTransaction(deleteOldFileCB, false, true);

        logger.debug("e) validate results");

        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                // assertNotNull("data is null", data);
                // assertEquals("size is wrong", 123904, data.getSize());

                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertTrue("file has lost versionable aspect", nodeService.hasAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE));

                assertEquals("mimeType is wrong", testContext.mimetype, data.getMimetype());

                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);
    } // Test Word 2011 Mountain Lion

    /**
     * This test tries to simulate the cifs shuffling that is done from Save from Mac Mountain Lion by Preview
     * 
     * a) Temp file created in temporary folder (Crysanthemum.jpg) b) Target file deleted by open / delete on close flag / close c) Temp file moved to target file.
     */
    public void testScenarioMacMountainLionPreview() throws Exception
    {
        logger.debug("testScenarioMountainLionPreview");
        final String FILE_NAME = "Crysanthemeum.jpg";
        final String TEMP_FILE_NAME = "Crysanthemeum.jpg";

        final String UPDATED_TEXT = "Mac Lion Preview Updated Content";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile tempFileHandle;
            NodeRef testNodeRef; // node ref Crysanthemenum.jpg
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionPreview";
        final String TEST_TEMP_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionPreview\\.Temporary Items";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);
                driver.createDirectory(testSession, testConnection, createTempDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                String testContent = "Mac Mountain Lion Text";
                byte[] testContentBytes = testContent.getBytes();

                driver.writeFile(testSession, testConnection, testContext.firstFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                /**
                 * Create the temp file we are going to use
                 */
                FileOpenParams createTempFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.tempFileHandle = driver.createFile(testSession, testConnection, createTempFileParams);
                assertNotNull(testContext.tempFileHandle);

                testContent = UPDATED_TEXT;
                testContentBytes = testContent.getBytes();
                driver.writeFile(testSession, testConnection, testContext.tempFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.tempFileHandle);

                /**
                 * Also add versionable to target file
                 */
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        // /**
        // * b) Delete the target file by opening it and set the delete on close bit
        // */
        // RetryingTransactionCallback<Void> deleteTargetFileCB = new RetryingTransactionCallback<Void>() {
        //
        // @Override
        // public Void execute() throws Throwable
        // {
        // FileOpenParams openFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
        // testContext.tempFileHandle = driver.openFile(testSession, testConnection, openFileParams);
        // FileInfo info = new FileInfo();
        // info.setFileInformationFlags(FileInfo.SetDeleteOnClose);
        // info.setDeleteOnClose(true);
        // testContext.tempFileHandle.setDeleteOnClose(true);
        //
        // driver.setFileInformation(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, info);
        //
        // assertNotNull(testContext.tempFileHandle);
        // logger.debug("this close should result in a file being deleted");
        // driver.closeFile(testSession, testConnection, testContext.tempFileHandle);
        // return null;
        // }
        // };
        // tran.doInTransaction(deleteTargetFileCB, false, true);

        /**
         * b) Delete the target file by a simple delete
         */
        RetryingTransactionCallback<Void> deleteTargetFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(deleteTargetFileCB, false, true);

        /**
         * c) Move the temp file into target directory
         */
        RetryingTransactionCallback<Void> moveTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(moveTempFileCB, false, true);

        /**
         * Validate results.
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertTrue("node is not versionable", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertEquals("shuffledNode ref is different", shuffledNodeRef, testContext.testNodeRef);
                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

    } // testScenarioMountainLionPreview

    /**
     * This test tries to simulate the cifs shuffling that is done from Save from Mac Mountain Lion by Preview when document is saved first time
     * 
     * a) Temp file created in temporary folder (temp\image.jpg.sb-1e5e1543-ajn3cR) b) Temp file renamed to original name (temp\image.jpg.sb-1e5e1543-ajn3cR -> temp\image.jpg) c) Original document renamed to backup copy name (test\image.jpg -> test\image.jpg.sb-1e5e1543-ajn3cR) d) Renamed temp file moved to original (temp\image.jpg -> test\image.jpg)
     */
    public void testScenarioMacMountainLionPreview_MNT_263() throws Exception
    {
        logger.debug("testScenarioMacMountainLionPreview_MNT_263");
        final String FILE_NAME = "image.jpg";
        final String TEMP_FILE_NAME = "image.jpg.sb-1e5e1543-ajn3cR";

        final String UPDATED_TEXT = "Mac Lion Preview Updated Content";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile tempFileHandle;
            NodeRef testNodeRef; // node ref image.jpg
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionPreview";
        final String TEST_TEMP_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionPreview\\.Temporary Items";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);
                driver.createDirectory(testSession, testConnection, createTempDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                String testContent = "Mac Lion Preview Content";
                byte[] testContentBytes = testContent.getBytes();

                driver.writeFile(testSession, testConnection, testContext.firstFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                /**
                 * Create the temp file we are going to use
                 */
                FileOpenParams createTempFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.tempFileHandle = driver.createFile(testSession, testConnection, createTempFileParams);
                assertNotNull(testContext.tempFileHandle);

                testContent = UPDATED_TEXT;
                testContentBytes = testContent.getBytes();
                driver.writeFile(testSession, testConnection, testContext.tempFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.tempFileHandle);

                /**
                 * Also add versionable to target file
                 */
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        RetryingTransactionCallback<Void> renameTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, TEST_TEMP_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(renameTempFileCB, false, true);

        RetryingTransactionCallback<Void> renameFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + TEMP_FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(renameFileCB, false, true);

        RetryingTransactionCallback<Void> moveRenamedTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(moveRenamedTempFileCB, false, true);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertTrue("node is not versionable", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertEquals("shuffledNode ref is different", shuffledNodeRef, testContext.testNodeRef);
                assertEquals("Unexpected content size", contentService.getReader(shuffledNodeRef, ContentModel.PROP_CONTENT).getSize(), UPDATED_TEXT.length());

                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

    } // testScenarioMacMountainLionPreview_MNT_263

    /**
     * This test tries to simulate the cifs shuffling that is done from Save from Mac Mountain Lion by Preview when document is opened/saved few time a row
     * 
     * a) Temp file created in temporary folder (temp\image.jpg) b) Original file is renamed for deletion(test\image.jpg -> test\.smbdeleteAAA1b994.4) c) Renamed file has got deleteOnClose flag d) Renamed file is closed. e) Temp file is moved into original file location(temp\image.jpg -> test\image.jgp)
     */
    public void testScenarioMacMountainLionPreview_MNT_317() throws Exception
    {
        logger.debug("testScenarioMacMountainLionPreview_MNT_317");
        final String FILE_NAME = "image.jpg";
        final String TEMP_FILE_NAME = ".smbdeleteAAA1b994.4";

        final String UPDATED_TEXT = "Mac Lion Preview Updated Content";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile tempFileHandle;
            NodeRef testNodeRef; // node ref image.jpg
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionPreview";
        final String TEST_TEMP_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionPreview\\.Temporary Items";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);
                driver.createDirectory(testSession, testConnection, createTempDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                String testContent = "Mac Lion Preview Content";
                byte[] testContentBytes = testContent.getBytes();

                driver.writeFile(testSession, testConnection, testContext.firstFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                /**
                 * Create the temp file we are going to use
                 */
                FileOpenParams createTempFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.tempFileHandle = driver.createFile(testSession, testConnection, createTempFileParams);
                assertNotNull(testContext.tempFileHandle);

                testContent = UPDATED_TEXT;
                testContentBytes = testContent.getBytes();
                driver.writeFile(testSession, testConnection, testContext.tempFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.tempFileHandle);

                /**
                 * Also add versionable to target file
                 */
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        RetryingTransactionCallback<Void> renameFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams openFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.tempFileHandle = driver.openFile(testSession, testConnection, openFileParams);
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + TEMP_FILE_NAME);

                return null;
            }
        };
        tran.doInTransaction(renameFileCB, false, true);

        /**
         * Delete file via deleteOnClose flag.
         */
        RetryingTransactionCallback<Void> deleteOnCloseCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileInfo info = new FileInfo();
                info.setFileInformationFlags(FileInfo.SetDeleteOnClose);
                info.setDeleteOnClose(true);
                testContext.tempFileHandle.setDeleteOnClose(true);

                driver.setFileInformation(testSession, testConnection, TEST_DIR + "\\" + TEMP_FILE_NAME, info);

                assertNotNull(testContext.tempFileHandle);
                logger.debug("this close should result in a file being deleted");
                driver.closeFile(testSession, testConnection, testContext.tempFileHandle);
                return null;
            }
        };
        tran.doInTransaction(deleteOnCloseCB, false, true);

        // /**
        // * Delete file directly.
        // */
        // RetryingTransactionCallback<Void> deleteTargetFileCB = new RetryingTransactionCallback<Void>()
        // {
        //
        // @Override
        // public Void execute() throws Throwable
        // {
        //
        // driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + TEMP_FILE_NAME);
        // return null;
        // }
        // };
        // tran.doInTransaction(deleteTargetFileCB, false, true);

        RetryingTransactionCallback<Void> moveRenamedTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(moveRenamedTempFileCB, false, true);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertTrue("node is not versionable", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertEquals("shuffledNode ref is different", shuffledNodeRef, testContext.testNodeRef);
                assertEquals("Unexpected content size", contentService.getReader(shuffledNodeRef, ContentModel.PROP_CONTENT).getSize(), UPDATED_TEXT.length());

                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

    } // testScenarioMacMountainLionPreview_MNT_317

    /**
     * This test tries to simulate the cifs shuffling that is done from Save from Mac Mountain Lion by Keynote when document is saved first time
     * 
     * a) Temp file created in temporary folder (temp\test.key) b) Original document renamed to backup copy name (test\test.key -> test\test~.key) c) Temp file moved to original name (temp\test.key -> test\test.key)
     */
    public void testScenarioMacMountainLionKeynote_MNT_8558() throws Exception
    {
        logger.debug("testScenarioMacMountainLionKeynote_MNT_8558");
        final String FILE_NAME = "test.key";
        final String BCKP_FILE_NAME = "test~.key";
        final String TEMP_FILE_NAME = "test.key";

        final String UPDATED_TEXT = "Mac Mountain Lion Keynote Updated Content";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile tempFileHandle;
            NodeRef testNodeRef;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionKeynote";
        final String TEST_TEMP_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionKeynote\\.Temporary Items";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);
                driver.createDirectory(testSession, testConnection, createTempDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                String testContent = "Mac Mountain Lion Keynote Content";
                byte[] testContentBytes = testContent.getBytes();

                driver.writeFile(testSession, testConnection, testContext.firstFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                /**
                 * Create the temp file we are going to use
                 */
                FileOpenParams createTempFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.tempFileHandle = driver.createFile(testSession, testConnection, createTempFileParams);
                assertNotNull(testContext.tempFileHandle);

                testContent = UPDATED_TEXT;
                testContentBytes = testContent.getBytes();
                driver.writeFile(testSession, testConnection, testContext.tempFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.tempFileHandle);

                /**
                 * Also add versionable to target file
                 */
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        RetryingTransactionCallback<Void> renameFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + BCKP_FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(renameFileCB, false, true);

        RetryingTransactionCallback<Void> moveTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(moveTempFileCB, false, true);

        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertTrue("node is not versionable", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertEquals("shuffledNode ref is different", shuffledNodeRef, testContext.testNodeRef);
                assertEquals("Unexpected content size", contentService.getReader(shuffledNodeRef, ContentModel.PROP_CONTENT).getSize(), UPDATED_TEXT.length());

                return null;
            }
        };

        tran.doInTransaction(validateCB, false, true);

        // Make sure that during second rename test.key->test~.key deleted test~.key is not restored and version history doesn't lost.
        RetryingTransactionCallback<Void> prepareForSecondRunCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + BCKP_FILE_NAME);
                FileOpenParams createTempFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.tempFileHandle = driver.createFile(testSession, testConnection, createTempFileParams);

                String testContent = UPDATED_TEXT;
                byte[] testContentBytes = testContent.getBytes();
                driver.writeFile(testSession, testConnection, testContext.tempFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.tempFileHandle);

                return null;
            }
        };

        tran.doInTransaction(prepareForSecondRunCB, false, true);
        tran.doInTransaction(renameFileCB, false, true);
        tran.doInTransaction(moveTempFileCB, false, true);
        tran.doInTransaction(validateCB, false, true);

    } // testScenarioMacMountainLionKeynote_MNT_8558

    /**
     * Gedit has the nasty behaviour of renaming an open file. 1) create file (gedit12345678.txt) 2) create temp file (.goutputStream-IRYDPW) write and flush 3) rename (fails name collision) 4) delete target 5) rename this one succeeds 6) close temp file
     * 
     */
    public void testGedit() throws Exception
    {
        logger.debug("testGEdit");

        final String FILE_NAME = "gedit12345678.txt";
        final String FILE_TITLE = "Gedit";
        final String FILE_DESCRIPTION = "This is a test document to test CIFS shuffle";
        final String TEMP_FILE_NAME = ".goutputStream-IRYDPW";
        final String UPDATE_TEXT = "Shuffle an open file";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile tempFileHandle;
            NodeRef testNodeRef;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testGEdit";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createTestFileFirstTime = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_TITLE, FILE_TITLE);
                nodeService.setProperty(testContext.testNodeRef, ContentModel.PROP_DESCRIPTION, FILE_DESCRIPTION);

                String testContent = "Gedit shuffle test";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);

                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createTestFileFirstTime, false, true);

        /**
         * Create the temp file add content leave open
         */
        RetryingTransactionCallback<Void> createTempFile = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {

                FileOpenParams params = new FileOpenParams(TEST_DIR + "\\" + TEMP_FILE_NAME, FileAction.TruncateExisting, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                NetworkFile file = driver.createFile(testSession, testConnection, params);
                testContext.tempFileHandle = file;
                String testContent = UPDATE_TEXT;
                byte[] testContentBytes = testContent.getBytes();
                testContext.tempFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                /** driver.closeFile(testSession, testConnection, file); **/

                return null;
            }
        };
        tran.doInTransaction(createTempFile, false, true);

        /**
         * rename the test file to the temp
         */
        RetryingTransactionCallback<Void> renameTestFileToTemp = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + TEMP_FILE_NAME, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        // expect this one to fail
        try
        {
            tran.doInTransaction(renameTestFileToTemp, false, true);
            fail("should have failed");
        }
        catch (Exception e)
        {
            // expect to go here
        }

        /**
         * delete the target file
         */
        RetryingTransactionCallback<Void> deleteTargetFile = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(deleteTargetFile, false, true);

        // This one should succeed
        tran.doInTransaction(renameTestFileToTemp, false, true);

        RetryingTransactionCallback<Void> closeTempFile = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.closeFile(testSession, testConnection, testContext.tempFileHandle);
                return null;
            }
        };

        tran.doInTransaction(closeTempFile, false, true);

        // Now validate
        RetryingTransactionCallback<Void> validate = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                ContentReader reader = contentService.getReader(shuffledNodeRef, ContentModel.PROP_CONTENT);
                String s = reader.getContentString();
                assertEquals("content not written", UPDATE_TEXT, s);

                assertTrue("node is not versionable", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertEquals("shuffledNode ref is different", shuffledNodeRef, testContext.testNodeRef);

                return null;
            }
        };

        tran.doInTransaction(validate, false, true);

        logger.debug("end testGedit");
    } // testGedit

    /**
     * Windows7 Explorer update 0) Existing file mark.jpg a) Create new file (~ark.tmp) b) Existing file rename out of the way. (mark.jpg~RF5bb356.TMP) c) New file rename into place. (~ark.tmp - mark.jpg) d) Old file opened attributes only e) set delete on close f) close
     */
    public void testWindows7Explorer() throws Exception
    {
        logger.debug("testWindows7Explorer");

        final String FILE_NAME = "mark.jpg";
        final String FILE_OLD_TEMP = "mark.jpg~RF5bb356.TMP";
        final String FILE_NEW_TEMP = "~ark.tmp";

        class TestContext
        {
            NodeRef testNodeRef;
            NetworkFile firstFileHandle;
            NetworkFile secondFileHandle;
        }
        ;

        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testWindows7Explorer";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("0) create new file");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTestMark.jpg");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTestMark.jpg", fileResource);
                writeResourceToNetworkFile(fileResource, testContext.firstFileHandle);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                testContext.testNodeRef = file1NodeRef;
                nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * a) Save the new file
         */
        logger.debug("a) save new file");
        RetryingTransactionCallback<Void> writeFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.secondFileHandle = driver.createFile(testSession, testConnection, createFileParams);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTestMark2.jpg");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTestMark2.jpg", fileResource);
                writeResourceToNetworkFile(fileResource, testContext.secondFileHandle);
                driver.closeFile(testSession, testConnection, testContext.secondFileHandle);

                return null;
            }
        };
        tran.doInTransaction(writeFileCB, false, true);

        /**
         * b) rename the old file
         */
        logger.debug("c) rename old file");
        RetryingTransactionCallback<Void> renameOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        tran.doInTransaction(renameOldFileCB, false, true);

        /**
         * c) Move the new file into place, stuff should get shuffled
         */
        logger.debug("d) move new file into place");
        RetryingTransactionCallback<Void> moveNewFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };

        tran.doInTransaction(moveNewFileCB, false, true);

        /**
         * d) Delete the old file
         */
        logger.debug("d) delete on close the old file");
        RetryingTransactionCallback<Void> deleteOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                FileOpenParams openFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_OLD_TEMP, 0, AccessMode.NTReadAttributesOnly, FileAttribute.NTNormal, 0);
                testContext.secondFileHandle = driver.openFile(testSession, testConnection, openFileParams);
                assertNotNull(testContext.secondFileHandle);

                FileInfo info = new FileInfo();
                info.setFileInformationFlags(FileInfo.SetDeleteOnClose);
                driver.setFileInformation(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP, info);
                testContext.secondFileHandle.setDeleteOnClose(true);

                driver.closeFile(testSession, testConnection, testContext.secondFileHandle);
                return null;
            }
        };

        tran.doInTransaction(deleteOldFileCB, false, true);

        logger.debug("e) validate results");

        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertTrue("file has lost versionable aspect", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));
                assertEquals("node ref has changed", shuffledNodeRef, testContext.testNodeRef);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);
                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                assertNotNull("data is null", data);
                assertEquals("size is wrong", 10407, data.getSize());
                assertEquals("mimeType is wrong", "image/jpeg", data.getMimetype());

                // TODO - test metadata extraction
                // assertEquals(false, props.get(QName.createQName(NamespaceService.EXIF_MODEL_1_0_URI, "flash")));

                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);
    } // Test Word 7 Explorer Update

    /**
     * 0. test.txt and ~test.txt exist. 1. Delete test.txt 2. Rename test.txt~ to test.txt
     */
    public void testNFS() throws Exception
    {
        logger.debug("testNFS()");

        final String FILE_NAME = "test.txt";
        final String FILE_NAME_TEMP = "test.txt~";

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile tempFileHandle;
            NodeRef file1NodeRef;
        }
        ;

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testNFS";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "cifs", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        RetryingTransactionCallback<Void> deleteGarbageFileCB2 = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME_TEMP);
                return null;
            }
        };

        /**
         * Create a file in the test directory
         */

        try
        {
            logger.debug("expect to get exception - cleaning garbage");
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }
        try
        {
            logger.debug("expect to get exception - cleaning garbage");
            tran.doInTransaction(deleteGarbageFileCB2);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("0) create new file");
        RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>() {
            @Override
            public TestContext execute() throws Throwable
            {

                TestContext ctx = new TestContext();
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to use
                 */
                {
                    FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                    ctx.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                    assertNotNull(ctx.firstFileHandle);
                    driver.closeFile(testSession, testConnection, ctx.firstFileHandle);
                    ctx.file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                }
                {
                    FileOpenParams createTempFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                    ctx.tempFileHandle = driver.createFile(testSession, testConnection, createTempFileParams);
                    assertNotNull(ctx.tempFileHandle);
                    driver.closeFile(testSession, testConnection, ctx.tempFileHandle);
                }
                return ctx;
            }
        };
        final TestContext testContext = tran.doInTransaction(setupCB, false, true);

        /**
         * 1) delete the old file
         */
        logger.debug("1) delete old file");
        RetryingTransactionCallback<Void> deleteOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(deleteOldFileCB, false, true);

        logger.debug("2) remame temp file");
        RetryingTransactionCallback<Void> renameTempFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        tran.doInTransaction(renameTempFileCB, false, true);

        logger.debug("3) validate results");
        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                // assertTrue("versionable aspect missing", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE));

                assertEquals("Node ref has changed", shuffledNodeRef, testContext.file1NodeRef);
                return null;
            }
        };

        tran.doInTransaction(validateCB, true, true);
        logger.debug("end testNFS");

    } // testNFS

    private void doTransactionWorkAsEditor(final RunAsWork<Void> work, RetryingTransactionHelper tran)
    {
        RetryingTransactionCallback<Void> transactionCallback = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable
            {
                try
                {
                    AuthenticationUtil.runAs(work, ContentDiskDriverTest.TEST_USER_AUTHORITY);
                }
                catch (Exception e)
                {
                    // Informing about test failure. Expected exception is 'AccessDeniedException' or 'PermissionDeniedException'
                    if (e.getCause() instanceof AccessDeniedException || e.getCause() instanceof PermissionDeniedException)
                    {
                        fail("For user='" + TEST_USER_AUTHORITY + "' " + e.getCause().toString());
                    }
                    else
                    {
                        fail("Unexpected exception was caught: " + e.toString());
                    }
                }
                return null;
            }
        };
        tran.doInTransaction(transactionCallback, false, true);
    }

    /**
     * 0. test.txt exist in folder where user has Editor permissions 1. as Editor create temporary file in temporary directory 2. as Editor rename test.txt to test.txt.sb-1eefba7a-rkC6XE 3. as Editor move temporary file to working directory
     */
    public void testScenarioMacLionTextEditByEditor_ALF_16257() throws Exception
    {
        logger.debug("test Collaborator/editor edit txt file on Mac Os Mountain Lion : Alf16257");
        final String FILE_NAME = "test.txt";
        final String FILE_BACKUP = "test.txt.sb-1eefba7a-rkC6XE";
        final String FILE_NEW_TEMP = FILE_NAME; // the same but in temp dir

        class TestContext
        {
            NetworkFile firstFileHandle;
            NetworkFile newFileHandle;
            NodeRef testNodeRef; // node ref of FILE_NAME
        }
        ;
        final TestContext testContext = new TestContext();

        final String TEST_ROOT_DIR = "\\ContentDiskDriverTest";
        final String TEST_DIR = TEST_ROOT_DIR + "\\testALF16257txt";
        final String TEST_TEMP_DIR = TEST_DIR + "\\.TemporaryItems";
        final String UPDATED_TEXT = "This is new content";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "cifs", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("Step 0 - initialise");
        /**
         * Create a file in the test directory
         */
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);
                driver.createDirectory(testSession, testConnection, createTempDirParams);

                /**
                 * Create the file we are going to use
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                // no need to test lots of different properties, that's already been tested above
                testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
                nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                String testContent = "CIFS: Collaborator/editor could not edit file on Mac Os Mountain Lion";
                byte[] testContentBytes = testContent.getBytes();
                testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                testContext.firstFileHandle.close();

                // Apply 'Editor' role for test user to test folder
                permissionService.setPermission(getNodeForPath(testConnection, TEST_DIR), ContentDiskDriverTest.TEST_USER_AUTHORITY, PermissionService.EDITOR, true);
                // Apply full control on temporary directory
                permissionService.setPermission(getNodeForPath(testConnection, TEST_TEMP_DIR), PermissionService.ALL_AUTHORITIES, PermissionService.ALL_PERMISSIONS, true);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * a) Save the temp file in the temp dir
         */
        logger.debug("Step a - create a temp file in the temp dir");
        RunAsWork<Void> saveNewFileCB = new RunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.newFileHandle);

                byte[] testContentBytes = UPDATED_TEXT.getBytes();
                driver.writeFile(testSession, testConnection, testContext.newFileHandle, testContentBytes, 0, testContentBytes.length, 0);
                driver.closeFile(testSession, testConnection, testContext.newFileHandle);

                NodeRef tempNodeRef = getNodeForPath(testConnection, TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP);
                ContentReader reader = contentService.getReader(tempNodeRef, ContentModel.PROP_CONTENT);
                assertNotNull(reader);
                String actualContent = reader.getContentString();
                assertEquals("new contents were not written to temporary file", UPDATED_TEXT, actualContent);
                return null;
            }
        };
        doTransactionWorkAsEditor(saveNewFileCB, tran);

        /**
         * b) rename the target file to a backup file
         */
        logger.debug("Step b - rename the target file as Editor");
        RunAsWork<Void> renameOldFileCB = new RunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_BACKUP);
                return null;
            }
        };
        doTransactionWorkAsEditor(renameOldFileCB, tran);

        /**
         * c) Move the new file into target dir, stuff should get shuffled
         */
        logger.debug("Step c - move new file into target dir as Editor");
        RunAsWork<Void> moveNewFileCB = new RunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception
            {
                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        doTransactionWorkAsEditor(moveNewFileCB, tran);

        /**
         * Validate
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);
                assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
                assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME));
                ContentReader reader = contentService.getReader(testContext.testNodeRef, ContentModel.PROP_CONTENT);
                assertNotNull(reader);
                String actualContent = reader.getContentString();
                assertEquals("contents were not updated", UPDATED_TEXT, actualContent);
                return null;
            }
        };
        tran.doInTransaction(validateCB, false, true);

        logger.debug("end testScenarioMacLionTextEditByEditor For ALF-16257");
    } // testScenarioMacLionTextEditByEditorForAlf16257

    /**
     * 0. MacWord1.docx exist in folder where user has Editor permissions 1. as Editor rename MacWord1.docx to backup Word Work File L_5.tmp 2. as Editor create temporary file in temporary directory and move it to working dir 3. as Editor rename Word Work File D_2.tmp to MacWord1.docx
     */
    public void testScenarioMountainLionWord2011EditByEditor_ALF_16257() throws Exception
    {
        logger.debug("testScenarioMountainLionWord2011 Edit By Editor ALF-16257");

        final String FILE_NAME = "MacWord1.docx";
        final String FILE_OLD_TEMP = "Word Work File L_5.tmp";
        final String FILE_NEW_TEMP = "Word Work File D_2.tmp";

        class TestContext
        {
            NetworkFile firstFileHandle;
            String mimetype;
        }
        ;
        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testALF16257Word";
        final String TEST_TEMP_DIR = TEST_DIR + "\\.TemporaryItems"; // need to match with interimPattern

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("a) create new file");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);
                driver.createDirectory(testSession, testConnection, createTempDirParams);

                /**
                 * Create the file we are going to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource);
                writeResourceToNetworkFile(fileResource, testContext.firstFileHandle);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);

                // Apply 'Editor' role for test user to test folder
                permissionService.setPermission(getNodeForPath(testConnection, TEST_DIR), ContentDiskDriverTest.TEST_USER_AUTHORITY, PermissionService.EDITOR, true);
                // Apply full control on temporary directory
                permissionService.setPermission(getNodeForPath(testConnection, TEST_TEMP_DIR), PermissionService.ALL_AUTHORITIES, PermissionService.ALL_PERMISSIONS, true);

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * b) rename the old file, should fire doubleRenameShuffle scenario
         */
        logger.debug("b) rename old file");
        RunAsWork<Void> renameOldFileCB = new RunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception
            {
                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                Map<QName, Serializable> props = nodeService.getProperties(file1NodeRef);
                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                testContext.mimetype = data.getMimetype();

                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };
        doTransactionWorkAsEditor(renameOldFileCB, tran);

        /**
         * c) as Editor Save the new file in .TemporaryItems and move it to working directory (should be detected by scenario) Write ContentDiskDriverTest3.doc,
         */
        logger.debug("c) create temp file in temp dir");
        RunAsWork<Void> writeFileCB = new RunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc");
                assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource);
                writeResourceToNetworkFile(fileResource, testContext.firstFileHandle);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NEW_TEMP);
                return null;
            }
        };
        doTransactionWorkAsEditor(writeFileCB, tran);

        /**
         * d) Move the new file into place, stuff should get shuffled
         */
        logger.debug("d) move new file into place");
        RunAsWork<Void> moveNewFileCB = new RunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception
            {
                driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        doTransactionWorkAsEditor(moveNewFileCB, tran);

        /**
         * e) Delete the old file
         */
        logger.debug("e) delete the old file");
        RetryingTransactionCallback<Void> deleteOldFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP);
                return null;
            }
        };

        tran.doInTransaction(deleteOldFileCB, false, true);

        logger.debug("e) validate results");

        logger.debug("f) validate results");
        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);

                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);

                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);
                // assertNotNull("data is null", data);
                // assertEquals("size is wrong", 123904, data.getSize());

                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                assertTrue("file has lost versionable aspect", nodeService.hasAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE));

                assertEquals("mimeType is wrong", testContext.mimetype, data.getMimetype());

                return null;
            }
        };
        tran.doInTransaction(validateCB, true, true);
        logger.debug("end testScenarioMountainLionWord2011 Edit By Editor ALF-16257");
    } // testScenarioMountainLionWord2011EditByEditor_ALF_16257

    /**
     * Guess mimetype with sufficient data - originally related to ACE-4523 Simulate creating a plain text document via Alfresco Share then updating it via CIFS/FTP
     * 
     * 1. create a document called "foo" with just a little data with an explicit (incorrect) mimetype set. 2. update the document with different text 3. check the mimetype of the test doc has changed (since it can be guessed)
     */
    public void testMimetypeWithSufficientData() throws Exception
    {
        logger.debug("testMimetypeWithInsufficiantData");

        // a file without a clue about mimetype
        final String FILE_NAME = "foo";

        class TestContext
        {
            NetworkFile firstFileHandle;
            String mimetype;
        }
        ;
        final TestContext testContext = new TestContext();

        final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testMimetypeWithSufficientData";

        // this is a made up mimetype - so there is no way that it could be guessed.
        final String TEST_MIMETYPE = "text\bar";

        ServerConfiguration scfg = new ServerConfiguration("testServer");
        TestServer testServer = new TestServer("testServer", scfg);
        final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
        DiskSharedDevice share = getDiskSharedDevice();
        final TreeConnection testConnection = testServer.getTreeConnection(share);
        final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();

        /**
         * Clean up just in case garbage is left from a previous run
         */
        RetryingTransactionCallback<Void> deleteGarbageFileCB = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable
            {
                driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME);
                return null;
            }
        };
        try
        {
            tran.doInTransaction(deleteGarbageFileCB);
        }
        catch (Exception e)
        {
            // expect to go here
        }

        logger.debug("a) create new file");
        RetryingTransactionCallback<Void> createFileCB = new RetryingTransactionCallback<Void>() {

            @Override
            public Void execute() throws Throwable
            {
                /**
                 * Create the test directory we are going to use
                 */
                FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                driver.createDirectory(testSession, testConnection, createRootDirParams);
                driver.createDirectory(testSession, testConnection, createDirParams);

                /**
                 * Create the file we are going to test
                 */
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
                assertNotNull(testContext.firstFileHandle);
                driver.closeFile(testSession, testConnection, testContext.firstFileHandle);

                ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTestTxt1.txt");
                // Add the test content via the content writer to simulate being created via Share.
                NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                ContentWriter contentWriter2 = contentService.getWriter(file1NodeRef, ContentModel.PROP_CONTENT, true);
                // this is a made up mimetype - so there is no way that it could be guessed.
                contentWriter2.setMimetype(TEST_MIMETYPE);
                contentWriter2.putContent(fileResource.getFile());

                return null;
            }
        };
        tran.doInTransaction(createFileCB, false, true);

        /**
         * b) Update the file via CIFS
         */
        logger.debug("b) update file via CIFS");
        RetryingTransactionCallback<Void> updateFileCB = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Exception
            {
                FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
                NetworkFile file = driver.openFile(testSession, testConnection, createFileParams);
                assertNotNull(file);
                String testContent = "Bar";
                byte[] testContentBytes = testContent.getBytes();
                file.writeFile(testContentBytes, testContentBytes.length, 0, 0);
                driver.closeFile(testSession, testConnection, file);
                return null;
            }
        };
        tran.doInTransaction(updateFileCB, false, true);

        logger.debug("c) validate results");

        /**
         * Now validate everything is correct
         */
        RetryingTransactionCallback<Void> validateCB = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable
            {
                NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
                Map<QName, Serializable> props = nodeService.getProperties(shuffledNodeRef);
                ContentData data = (ContentData) props.get(ContentModel.PROP_CONTENT);

                /**
                 * Validate mimetype has changed - we can guess that it is text !
                 */
                assertEquals("mimeType is wrong", MimetypeMap.MIMETYPE_TEXT_PLAIN, data.getMimetype());

                return null;
            }
        };
        tran.doInTransaction(validateCB, true, true);
        logger.debug("end testMimetypeWithSufficientData");
    } // testMimetypeWithSufficientData"

    /**
     * Test server
     */
    public class TestServer extends NetworkFileServer
    {

        public TestServer(String proto, ServerConfiguration config)
        {
            super(proto, config);
            // TODO Auto-generated constructor stub
        }

        @Override
        public void startServer()
        {

        }

        @Override
        public void shutdownServer(boolean immediate)
        {

        }

        public TreeConnection getTreeConnection(SharedDevice share)
        {
            return new TreeConnection(share);
        }
    }

    /**
     * TestSrvSession
     */
    private class TestSrvSession extends SrvSession
    {

        public TestSrvSession(int sessId, NetworkServer srv, String proto,
                String remName)
        {
            super(sessId, srv, proto, remName);

            // Set the client info to user "fred"
            ClientInfo cinfo = ClientInfo.createInfo("fred", null);
            setClientInformation(cinfo);
            setUniqueId("test:" + sessId);

        }

        @Override
        public InetAddress getRemoteAddress()
        {
            return null;
        }

        @Override
        public boolean useCaseSensitiveSearch()
        {
            return false;
        }
    }

    private NodeRef getNodeForPath(TreeConnection tree, String path)
            throws FileNotFoundException
    {
        if (logger.isDebugEnabled())
        {
            logger.debug("getNodeRefForPath:" + path);
        }

        ContentContext ctx = (ContentContext) tree.getContext();

        return cifsHelper.getNodeRef(ctx.getRootNode(), path);
    }

    /**
     * Write the resource to the specified NetworkFile
     * 
     * @param resource
     * @param file
     * @throws IOException
     */
    private void writeResourceToNetworkFile(ClassPathResource resource, NetworkFile file) throws IOException
    {

        byte[] buffer = new byte[1000];
        InputStream is = resource.getInputStream();
        try
        {
            long offset = 0;
            int i = is.read(buffer, 0, buffer.length);
            while (i > 0)
            {
                file.writeFile(buffer, i, 0, offset);
                offset += i;
                i = is.read(buffer, 0, buffer.length);
            }
        }
        finally
        {
            is.close();
        }
    }
}
